/* * Copyright 2003-2004, Franz-Josef Elmer, All rights reserved * * 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 program 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 * (http://www.gnu.org/copyleft/lesser.html). * * 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 jcckit.data; import java.text.MessageFormat; import java.util.Vector; /** * Abstract superclass of all data containers. A data container holds an * ordered list of {@link DataElement DataElements} of the same type. *

* Data elements can be added, inserted, removed, or replaced. * Such an action leads to a {@link DataEvent} which will be delivered to * all {@link DataListener DataListeners} observing this * DataContainer. If this data container also implements * {@link DataEvent} (as {@link DataCurve} does) also the listeners * registrated at the data container containg this container will be notified. * As a consequence a DataListener must only be registered at the * {@link DataPlot} instance and it will automatically also received events * caused by manipulating one of its DataCurves. *

* Concrete subclasses have to implement {@link #isValid} which * checks whether the added or inserted DataElement is of the right * type. This is an application of the Template Method Design Pattern. * * @author Franz-Josef Elmer */ public abstract class DataContainer { private final static String TEMPLATE = "Invalid operation: {0}, Element: {1}, Container: {2}"; final static String ADD = "add", REPLACE = "replace", INSERT = "insert"; private final Vector _listeners = new Vector(); private final Vector _container = new Vector(); /** Adds a {@link DataListener}. Does nothing if already added. */ public void addDataListener(DataListener listener) { if (!_listeners.contains(listener)) { _listeners.addElement(listener); } } /** Removes a {@link DataListener}. Does nothing if already removed. */ public void removeDataListener(DataListener listener) { _listeners.removeElement(listener); } private void notifyListeners(DataEvent event) { for (int i = 0, n = _listeners.size(); i < n; i++) { ((DataListener) _listeners.elementAt(i)).dataChanged(event); } // Notifies also parent container if (this instanceof DataElement) { DataContainer container = ((DataElement) this).getContainer(); if (container != null) { container.notifyListeners(event); } } } /** Returns the number of elements of this container. */ public int getNumberOfElements() { return _container.size(); } /** Returns the element for the specified index. */ public DataElement getElement(int index) { return (DataElement) _container.elementAt(index); } /** * Returns the index of the specified element. * @param element Element to be looked for. * @return -1 if not found. */ public int getIndexOf(DataElement element) { return _container.indexOf(element); } /** * Adds a {@link DataElement}. After the element has been successfully * added all {@link DataListener DataListeners} will be informed. * @param element DataElement to be added. * @throws IllegalArgumentException if element is not of the correct * type which will be checked by the method {@link #isValid}. */ public void addElement(DataElement element) { if (isValid(element)) { _container.addElement(element); element.setContainer(this); notifyListeners(DataEvent.createAddEvent(this)); } else { throwException(ADD, element); } } /** * Inserts a {@link DataElement} at the specified index. * After the element has been successfully inserted * all {@link DataListener DataListeners} will be informed. * @param index Index at which element will be inserted. * All elements with an index >= index will be shifted. * @param element DataElement to be added. * @throws IllegalArgumentException if element is not of the correct * type which will be checked by the method {@link #isValid}. */ public void insertElementAt(int index, DataElement element) { if (isValid(element)) { _container.insertElementAt(element, index); element.setContainer(this); notifyListeners(DataEvent.createInsertEvent(this, index)); } else { throwException(INSERT, element); } } /** * Removes a {@link DataElement} at the specified index. * After the element has been successfully removed * all {@link DataListener DataListeners} will be informed. * @param index Index of the element which will be removed. * All elements with an index > index will be shifted. */ public void removeElementAt(int index) { DataElement element = (DataElement) _container.elementAt(index); element.setContainer(null); _container.removeElementAt(index); notifyListeners(DataEvent.createRemoveEvent(this, index, element)); } /** * Replaces the {@link DataElement} at the specified index. * After the element has been successfully replaced * all {@link DataListener DataListeners} will be informed. * @param index Index of the element which will be replaced by * element. * @param element The new DataElement. * @throws IllegalArgumentException if element is not of the correct * type which will be checked by the method {@link #isValid}. */ public void replaceElementAt(int index, DataElement element) { if (isValid(element)) { DataElement oldElement = (DataElement) _container.elementAt(index); oldElement.setContainer(null); _container.setElementAt(element, index); element.setContainer(this); notifyListeners(DataEvent.createReplaceEvent(this, index, oldElement)); } else { throwException(REPLACE, element); } } private void throwException(String operation, DataElement element) { throw new IllegalArgumentException(MessageFormat.format(TEMPLATE, new Object[] {operation, element, this.getClass().getName()})); } /** * Returns true if the specified {@link DataElement} has the * correct type. Concrete subclasses have to implement this method. * @param element DataElement to be checked. */ protected abstract boolean isValid(DataElement element); }