/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* 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.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: DataSourceCollection.java,v 1.15 2008/09/08 10:12:57 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package simtools.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
import simtools.data.buffer.Buffer;
import simtools.data.buffer.BufferedDataSource;
import simtools.data.buffer.ResizeableBuffer;
import simtools.util.ListenerManager;
import simtools.util.NumberStringComparator;
/**
* A data source collection gathers data sources with strong interdependancy For
* example, all the data source values may be read from the same file.
*
* This strong inter-dependancy is highlighted by: - The delegation of
* CollectiveDataSource methods here - The same indexing system for all
* CollectiveDataSource - A common update mechanism.
*
* This is an abstract class. Default implementation handles indexing and update
* mechanism, but subclasses are responsible for handling values together with
* setting the index corresponding to those values.
*
* Two sets of methods are provided: one uses CollectiveDataSource parameters
* and the other uses integer = position in the vector of the corresponding
* CollectiveDataSource. They are of course equivalent and the first calls the
* second. Thus, subclasses should only implement methods with the vector-like
* API, not the ones with the CollectiveDataSource parameters.
*/
public abstract class DataSourceCollection extends Vector {
// listener helpers to avoid code dup
protected ListenerManager listeners = new ListenerManager();
protected ListenerManager endNotificationListeners = new ListenerManager();
// Index
protected long startIndex, lastIndex;
protected Buffer[] buffers;
/**
* to get quickly the StreamingDataSource in the vector according to its
* name
*/
protected HashMap map = null;
public DataSourceCollection() {
super();
startIndex = lastIndex = -1;
// initialize cached index with negative value => not cached
buffers = null;
}
/**
* Common indexation system for all members of this collection
*/
public long getStartIndex() throws UnsupportedOperation {
if (startIndex >= 0) {
return startIndex;
}
throw new UnsupportedOperation();
}
public long getLastIndex() throws UnsupportedOperation {
if (lastIndex >= 0) {
return lastIndex;
}
throw new UnsupportedOperation();
}
public long computeStartIndex() throws UnsupportedOperation {
if (startIndex >= 0) {
return startIndex;
}
throw new UnsupportedOperation();
}
public long computeLastIndex() throws UnsupportedOperation {
if (lastIndex >= 0) {
return lastIndex;
}
throw new UnsupportedOperation();
}
// Same, but for individual Sources => default is that index is common
public long getStartIndex(int i) throws UnsupportedOperation {
return getStartIndex();
}
public long getLastIndex(int i) throws UnsupportedOperation {
return getLastIndex();
}
public long computeStartIndex(int i) throws UnsupportedOperation {
return computeStartIndex();
}
public long computeLastIndex(int i) throws UnsupportedOperation {
return computeLastIndex();
}
public void setSlice(long min, long max) {
// propagate info to buffers
if (buffers != null) {
for (int i = 0; i < size(); i++) {
if (buffers[i] != null) {
buffers[i].setSlice(min, max);
// Do nothing else by default, subclass to decide what to do
}
}
}
}
// Our own information
public DataInfo getInformation() {
return null;
}
/**
* Kind of the data source at position i, Object by default
*
* @return the i-th data source kind
*/
public int getKind(int i) {
return ValueProvider.ObjectProvider;
}
/**
* Returns the more specialized superclass of all objects returned by the
* i-th data source. Returns Object by default in the base implementation,
* subclass should specialize if possible.
*/
public Class valueClass(int i) {
return Object.class;
}
/**
* @param id
* @return data source related to a string id
*/
public DataSource get(String id) {
if (map != null) {
// Should be the fastest way to get a DataSource from a string id
return (DataSource) map.get(id);
} else {
if (id == null) {
return null;
}
for (int j = 0; j < size(); j++) {
DataSource ds = (DataSource) get(j);
if (id.equals(DataInfo.getId(ds))) {
return ds;
}
}
return null;
}
}
/**
* Buffer methods
*/
/**
* Internal method for the buffer package only, could lead to
* inconsistencies if misused. User classes should use bufferize instead.
*/
public void attachBuffer(int i, Buffer b) {
if (buffers == null) {
buffers = new Buffer[size()];
}
buffers[i] = b;
}
/**
* Public method to put a buffer on a data source. This function uses the
* default buffer for this collection.
*
* @param i
* The position of the datasource to bufferize
*/
public void bufferize(int i) throws UnsupportedOperation {
bufferize(i, new ResizeableBuffer());
}
/**
* Public method to put a buffer on a data source
*
* @param i
* The position of the datasource to bufferize
* @param b
* The buffer to use. It must have been created using the
* datasource at position i, or a null provider (in which case,
* the datasource at position i will be used automatically). An
* UnsupportedOperation exception is thrown otherwise.
*/
public void bufferize(int i, Buffer b) throws UnsupportedOperation {
if (get(i) instanceof BufferedDataSource) {
return; // done already
}
if ((b.getProvider() != null) && (!b.getProvider().equals(get(i)))) {
throw new UnsupportedOperation();
}
b.setProvider((ValueProvider) get(i));
// this will in turn call back attachBuffer
set(i, new BufferedDataSource(b));
}
/**
* Public method to put a buffer on a data source
*
* @param b
* The buffer to use. It must have been created using a null
* provider. The buffer will then be cloned and applied to all
* datasources using the bufferize(int,Buffer) function.
*/
public void bufferize(Buffer b) throws UnsupportedOperation {
if (b.getProvider() != null) {
throw new UnsupportedOperation();
}
for (int i = 0; i < size(); i++) {
bufferize(i, (Buffer) b.clone());
}
}
/**
* Methods for subclasses to specialize
*/
public abstract Object getValue(int i, long index) throws DataException;
// / /** Optimized accessor for type values. Works the same way as provider
// functions.
// / */
// / public type getTypeValue(int i, long index) throws DataException {
// / Object o = getValue(i, index);
// / if (o == null) return 0;
// / if (o instanceof String) o =
// NumberStringComparator.stringToNumber((String)o);
// / if (o == null) return 0;
// / if (o instanceof Number) return ((Number)o).typeValue();
// / return 0;
// / }
// /
// ------ START of perl-generated corresponding code --------
/**
* Optimized accessor for byte values. Works the same way as provider
* functions.
*/
public byte getByteValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).byteValue();
}
return 0;
}
/**
* Optimized accessor for short values. Works the same way as provider
* functions.
*/
public short getShortValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).shortValue();
}
return 0;
}
/**
* Optimized accessor for int values. Works the same way as provider
* functions.
*/
public int getIntegerValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).intValue();
}
return 0;
}
/**
* Optimized accessor for long values. Works the same way as provider
* functions.
*/
public long getLongValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).longValue();
}
return 0;
}
/**
* Optimized accessor for float values. Works the same way as provider
* functions.
*/
public float getFloatValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).floatValue();
}
return 0;
}
/**
* Optimized accessor for double values. Works the same way as provider
* functions.
*/
public double getDoubleValue(int i, long index) throws DataException {
Object o = getValue(i, index);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).doubleValue();
}
return 0;
}
// -------- END of perl-generated corresponding code --------
public DataInfo getInformation(int i) {
return null;
}
public Object getMin(int i) throws UnsupportedOperation {
throw new UnsupportedOperation();
}
public Object getMax(int i) throws UnsupportedOperation {
throw new UnsupportedOperation();
}
// / public type getTypeMin(int i) throws DataException {
// / Object o = getMin(i);
// / if (o == null) return 0;
// / if (o instanceof String) o =
// NumberStringComparator.stringToNumber((String)o);
// / if (o == null) return 0;
// / if (o instanceof Number) return ((Number)o).typeValue();
// / return 0;
// / }
// /
// / public type getTypeMax(int i) throws DataException {
// / Object o = getMax(i);
// / if (o == null) return 0;
// / if (o instanceof String) o =
// NumberStringComparator.stringToNumber((String)o);
// / if (o == null) return 0;
// / if (o instanceof Number) return ((Number)o).typeValue();
// / return 0;
// / }
// /
// ------ START of perl-generated corresponding code --------
public byte getByteMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).byteValue();
}
return 0;
}
public byte getByteMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).byteValue();
}
return 0;
}
public short getShortMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).shortValue();
}
return 0;
}
public short getShortMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).shortValue();
}
return 0;
}
public int getIntegerMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).intValue();
}
return 0;
}
public int getIntegerMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).intValue();
}
return 0;
}
public long getLongMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).longValue();
}
return 0;
}
public long getLongMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).longValue();
}
return 0;
}
public float getFloatMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).floatValue();
}
return 0;
}
public float getFloatMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).floatValue();
}
return 0;
}
public double getDoubleMin(int i) throws DataException {
Object o = getMin(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).doubleValue();
}
return 0;
}
public double getDoubleMax(int i) throws DataException {
Object o = getMax(i);
if (o == null) {
return 0;
}
if (o instanceof String) {
o = NumberStringComparator.stringToNumber((String) o);
}
if (o == null) {
return 0;
}
if (o instanceof Number) {
return ((Number) o).doubleValue();
}
return 0;
}
// -------- END of perl-generated corresponding code --------
public Object computeMin(int i) throws UnsupportedOperation {
throw new UnsupportedOperation();
}
public Object computeMax(int i) throws UnsupportedOperation {
throw new UnsupportedOperation();
}
public int sortedOrder(int i) {
DataSource ds = (DataSource) get(i);
if (ds.getSortedOrder() == -2) {
ds.computeSortedOrder();
}
return ds.getSortedOrder();
}
public boolean isComparable(int i) {
return false; // not comparable by default
}
// Listeners related functions
public void addListener(DataSourceCollectionListener dscl) {
listeners.add(dscl);
}
public void removeListener(DataSourceCollectionListener dscl) {
listeners.remove(dscl);
}
public void addEndNotificationListener(EndNotificationListener enl) {
endNotificationListeners.add(enl);
}
public void removeEndNotificationListener(EndNotificationListener enl) {
endNotificationListeners.remove(enl);
}
// Listeners related functions : helpers for child classes
public void notifyListenersForInfoChange(DataInfo newInfo) {
synchronized (listeners) {
int n = listeners.size(); // only one call outside loop
for (int i = 0; i < n; ++i) {
DataSourceCollectionListener dscl = (DataSourceCollectionListener) listeners.get(i);
if (dscl != null) {
dscl.DataSourceCollectionInfoChanged(this, newInfo);
}
}
}
}
protected void notifyListenersForDataSourceAdded(DataSource ds) {
synchronized (listeners) {
int n = listeners.size(); // only one call outside loop
for (int i = 0; i < n; ++i) {
DataSourceCollectionListener dscl = (DataSourceCollectionListener) listeners.get(i);
if (dscl != null) {
dscl.DataSourceCollectionDataSourceAdded(this, ds);
}
}
}
}
protected void notifyListenersForDataSourceRemoved(DataSource ds) {
synchronized (listeners) {
int n = listeners.size(); // only one call outside loop
for (int i = 0; i < n; ++i) {
DataSourceCollectionListener dscl = (DataSourceCollectionListener) listeners.get(i);
if (dscl != null) {
dscl.DataSourceCollectionDataSourceRemoved(this, ds);
}
}
}
}
protected void notifyListenersForCollectionRemoved() {
synchronized (listeners) {
int n = listeners.size(); // only one call outside loop
for (int i = 0; i < n; ++i) {
DataSourceCollectionListener dscl = (DataSourceCollectionListener) listeners.get(i);
if (dscl != null) {
dscl.DataSourceCollectionRemoved(this);
}
}
}
}
public void removeAllElements() {
// Notify data sources listeners, that data has been removed
for (int i = 0; i < size(); i++) {
DataSource dsToRemove = (DataSource) get(i);
dsToRemove.notifyListenersForDataReplaced(null);
}
// Notify collection listener that collection has been removed
notifyListenersForCollectionRemoved();
// Delete collection contents
super.removeAllElements();
}
// Note: "listeners are called only once" is guaranteed by implementation :
// by using a reference
// counter in add/remove methods, and by delegating from the collective
// sources to this collection.
// So, there is nothing special to do at this point, which is what we want
// to keep the notification
// fast
/**
* Use this method only when this data source collection is the referer. See
* class comments for EndNotificationListener. Listeners for data sources in
* this collection will also be notified, but only once (if a listener
* listens to more than one data source in this collection, it is notified
* once).
*/
public void notifyEndNotificationListeners() {
synchronized (endNotificationListeners) {
int n = endNotificationListeners.size(); // only one call outside
// loop
for (int i = 0; i < n; ++i) {
EndNotificationListener enl = (EndNotificationListener) endNotificationListeners.get(i);
if (enl != null) {
enl.notificationEnd(this);
}
}
}
}
private final Object hashCode = new Object();
/**
* Overrides Vector/AbstractList implementation to get a fix hashcode and be
* able to put and remove a collection from a HashSet for instance
*/
public synchronized int hashCode() {
return hashCode.hashCode();
}
/**
* Checks if a data source collection is compound, i.e. if it provides
* containers to organize the data source hierarchically
*
* @return true if it is compound
*/
public boolean isCompound() {
return false;
}
/**
* If the data source collection is compound return the first level
* collection of containers used to organize the data sources
*
* @return the first level collection of containers or null
*/
public Collection getCollectionContainers() {
return null;
}
//
public class Container {
protected final String value;
protected Collection children;
/**
* If true, this container does not reflect the data name
* and shall be squizzed when filtering the data source collection tree
* with a data source ID
*/
protected boolean isVirtual;
/**
* Create a new container with the given value
*/
public Container(String value) {
this(value, false);
}
/**
* Create a new container with the given value
*/
public Container(String value, boolean isVirtual) {
this.value = value;
this.isVirtual = isVirtual;
}
public void addDataSource(DataSource ds) {
if (children == null) {
children = new ArrayList();
}
children.add(ds);
}
public void addContainer(Container c) {
if (children == null) {
children = new ArrayList();
}
children.add(c);
}
public void setChildren(Collection c) {
children = c;
}
public Collection getChildren() {
return children;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString() {
return value;
}
public boolean isVirtual() {
return isVirtual;
}
}
}