/*
 This file is part of the software framework JStatCom, 
 a library for building applications for data based analysis.

 Copyright (C) 2000-2005  Alexander Benkwitz, Markus Kraetzig

 This library is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as
 published by the Free Software Foundation; either version 2.1 of the
 License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 */

package com.jstatcom.ts;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.ListModel;

import com.jstatcom.component.TopFrameReference;
import com.jstatcom.model.JSCData;
import com.jstatcom.model.JSCDate;
import com.jstatcom.model.JSCNArray;
import com.jstatcom.model.JSCString;
import com.jstatcom.project.ProjectData;
import com.jstatcom.project.ProjectDataHandler;
import com.jstatcom.project.ProjectDataTypes;

/**
 * Holds all available TS at runtime. The identifying keys are
 * the names of the time series. The names are converted to lowercase, which
 * makes the lookup case insensitive. It also provides methods for adding,
 * removing, and reading of time series. This class is designed as a Singleton
 * (Gamma et al., 1995)  and can be accessed by the static
 * getInstance method.
 * 

* Classes can register themselves as PropertyChangeListeners to * the TSHolder if they should be notified when * TS_ADDED or TS_REMOVED events happen. *

* This class is designed to be thread-save for adding, removing, and reading of * time series. * * @author A. Benkwitz, Markus Kraetzig * @see TS * @see TSList */ public class TSHolder implements ProjectDataHandler { /** * Singleton instance. */ private static final TSHolder tsHolder = new TSHolder(); /** * PropertyChange values. */ public static final String TS_ADDED = "TS_ADDED"; public static final String TS_REMOVED = "TS_REMOVED"; public static final String ALL_TS_REMOVED = "ALL_TS_REMOVED"; /** * Holds PropertyChangeListeners. */ private final Vector propertyChangeListeners = new Vector(); /** * HashTable for storing TS. */ private final Map tsHashTable = new HashMap(); private final TSListModel tsListModel = new TSListModel(); /** * For representing the map as a ListModel with ordered names. */ private static class TSListModel extends DefaultListModel { /** * TSListModel takes care of sorting, when a new TS is * inserted. * * @param obj * to add, should be the name of a TS */ public void addElement(Object obj) { if (getSize() == 0) { super.addElement(obj); return; } String toAdd = obj.toString(); String toCompare = toAdd.toUpperCase(); // Add alphabetically sorted. for (int i = 0; i < getSize(); i++) { if (getElementAt(i).toString().toUpperCase().compareTo( toCompare) >= 0) { super.add(i, toAdd); return; } } // If it was not yet added, append it to the end. super.addElement(obj); } /** * Removes string element regardless for case. * * @param obj * the object to be removes */ public boolean removeElement(Object obj) { if (super.removeElement(obj)) return true; String toAdd = obj.toString(); // Add alphabetically sorted. for (int i = 0; i < getSize(); i++) { String obj_i = getElementAt(i).toString(); if (obj_i.equalsIgnoreCase(toAdd)) { return super.removeElement(obj_i); } } return false; } }; /** * TSHolder constructor. */ private TSHolder() { super(); } /** * */ public synchronized void addPropertyChangeListener( PropertyChangeListener listener) { if (!propertyChangeListeners.contains(listener)) propertyChangeListeners.add(listener); } /** * Clears all currently stored time series and adds the series contained in * newTimeSeries. * * @param newTimeSeries * array of time series to add */ public void setTS(TS[] newTimeSeries) { if (newTimeSeries == null) throw new IllegalArgumentException("Argument was null."); synchronized (this) { removeAllTS(); for (int i = 0; i < newTimeSeries.length; i++) { addTS(newTimeSeries[i]); } } } /** * Adds a new TS to this TSHolder. If a * series has less than 2 observations, an error message is displayed. If a * series with the same name already exists, a question is displayed, * whether the existing series should be overwritten. This method returns * JOptionPane.YES_OPTION if newTimeSeries was * null. *

* If the series was added, a TS_ADDED PropertyChangeEvent is * fired. * * @param newTimeSeries * the new TS to add * @return One of JOptionPane.YES_OPTION, * JOptionPane.NO_OPTION or * JOptionPane.CANCEL_OPTION, which the calling * method can process accordingly. If * JOptionPane.YES_OPTION is returned, the time * series was added successfully. */ public int addTS(TS newTimeSeries) { int accept = JOptionPane.YES_OPTION; // Do nothing if null. if (newTimeSeries == null) return accept; // No Lowercase for list display. String newNameOrig = newTimeSeries.name(); // Lowercase for lookup in holder. String newName = newNameOrig.toLowerCase(); // Check whether minimum condition is satisfied. if (newTimeSeries.numOfObs() < 2) { JOptionPane .showConfirmDialog( TopFrameReference.getTopFrameRef(), "Variable \"" + newName + "\" contains less than 2 observations.\nIt will not be added.", "Error", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE); return JOptionPane.NO_OPTION; } synchronized (this) { // Look whether key is already used. if (tsHashTable.containsKey(newName)) { accept = JOptionPane.showConfirmDialog(TopFrameReference .getTopFrameRef(), "Variable name \"" + newNameOrig + "\" already exists, overwrite?", "Question", JOptionPane.YES_NO_CANCEL_OPTION); } // Add the new time series to the time series table. if (accept == JOptionPane.YES_OPTION) { tsListModel.removeElement(newNameOrig); tsHashTable.put(newName, newTimeSeries); tsListModel.addElement(newNameOrig); } } // Outside the synchronized region. if (accept == JOptionPane.YES_OPTION) firePropertyChange(TS_ADDED, newName, newName); return accept; } /** * Returns whether the TSHolder contains a TS * with the given name. The lookup is case insensitive. * * @return true if series is contained, false otherwise * @param name * String with the name to be looked up */ public boolean contains(String name) { if (name != null) name = name.toLowerCase(); return tsHashTable.containsKey(name); } /** * Notifies all PropertyChangeListeners. * * @param propName * String * @param oldValue * Object * @param newValue * Object */ private void firePropertyChange(String propName, Object oldValue, Object newValue) { PropertyChangeEvent evt = new PropertyChangeEvent(this, propName, oldValue, newValue); for (int i = 0; i < propertyChangeListeners.size(); i++) propertyChangeListeners.get(i).propertyChange(evt); } /** * Gets TSHolder represented as a ListModel * with the names in alphabetical order. * * @return the ListModel that is in sync with this object */ public synchronized ListModel getAsListModel() { return tsListModel; } /** * Returns Singleton instance of a TSHolder. * * @return TSHolder instance to be shared */ public static TSHolder getInstance() { return tsHolder; } /** * Retrieves a time series for a given name. * * @param name * of the time series to get * @return TS with the given name, null if it * does not exist */ public synchronized TS getTS(String name) { if (name != null) name = name.toLowerCase(); if (tsHashTable.containsKey(name)) return tsHashTable.get(name); return null; } /** * Gets all time series. * * @return array of all series */ public synchronized TS[] getAllTS() { TS ts[] = new TS[tsHashTable.size()]; int i = 0; for (TS tsEl : tsHashTable.values()) ts[i++] = tsEl; return ts; } /** * Returns names of all available time series. * * @return iterator with names of all hashed TS */ public Iterator getTSNames() { return tsHashTable.keySet().iterator(); } /** * */ public synchronized void removePropertyChangeListener( PropertyChangeListener listener) { if (propertyChangeListeners.contains(listener)) propertyChangeListeners.remove(listener); } /** * Removes a time series from the map and fires a * TS_REMOVED PropertyChangeEvent. Nothing happens, if no * series with the given name was stored. * * @param name * of the time series to get */ public void removeTS(String name) { boolean fireChanged = false; synchronized (this) { if (tsHashTable.containsKey(name.toLowerCase())) { tsListModel.removeElement(name); tsHashTable.remove(name.toLowerCase()); fireChanged = true; } } // Outside the synchronized region. if (fireChanged) firePropertyChange(TS_REMOVED, name, name); } /** * Removes all time series from the map and fires an * ALL_TS_REMOVED PropertyChangeEvent if there were any * series removed. */ public void removeAllTS() { synchronized (this) { if (tsHashTable.size() == 0) return; tsListModel.clear(); tsHashTable.clear(); } // Outside the synchronized region. firePropertyChange(ALL_TS_REMOVED, null, null); } /** * String representation of this TSHolder. * * @return string containing name and names of all included TS */ public String toString() { StringBuffer ret = new StringBuffer(super.toString() + " ["); for (String name : tsHashTable.keySet()) { ret.append("\"" + name + "\","); } if (tsHashTable.size() > 0) ret.deleteCharAt(ret.length() - 1); ret.append("]"); return ret.toString(); } /** * Sets the project data to this shared data repository for time series. * * @param pData * project data to set */ public void setProjectData(ProjectData pData) { if (pData == null) return; TSHolder.getInstance().removeAllTS(); JSCData[] data = pData.getJSCData(); int tsCount = data.length / 4; if (tsCount == 0) return; String description = pData.getDescription(); TSProject project = new TSProject("TS_DATA", description); TS ts[] = new TS[tsCount]; for (int i = 0, k = 0; i < tsCount; i++) { JSCData data_k = data[k++]; String name = ((JSCString) data_k).string(); data_k = data[k++]; TSTypes tsType = TSTypes.valueOf(((JSCString) data_k).string()); data_k = data[k++]; TSDate start = ((JSCDate) data_k).getTSDate(); data_k = data[k++]; double[] values = ((JSCNArray) data_k).getCol(0); ts[i] = new TS(values, name, start, tsType, project); } setTS(ts); } /** * Gets the project data from this shared data repository. Typically this * method is used for serialization. * * @return project data assembled from this data repository */ public ProjectData getProjectData() { TS[] ts = getAllTS(); JSCData[] rtn = new JSCData[ts.length * 4]; List projectList = new ArrayList(); StringBuffer des = new StringBuffer(); for (int i = 0, k = 0; i < ts.length; i++) { if (!projectList.contains(ts[i].project())) { TSProject tmp = ts[i].project(); projectList.add(tmp); if (tmp != null) des.append(tmp.getDescription()); } rtn[k++] = new JSCString("ts_name_" + i, ts[i].name()); rtn[k++] = new JSCString("ts_type_" + i, ts[i].type().getName()); rtn[k++] = new JSCDate("ts_start_" + i, ts[i].start()); rtn[k++] = new JSCNArray("ts_data_" + i, ts[i].values()); } ProjectData data = new TSProjectData(); // get description if (ts.length > 0) data.setDescription(des.toString()); data.setJSCData(rtn); return data; } /** * Returns time series data type. * * @return ProjectDataTypes.TS_DATA */ public ProjectDataTypes getProjectDataType() { return ProjectDataTypes.TS_DATA; } }

Ads help maintain this website.