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

 Copyright (C) 2000-2005  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.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javolution37.javolution.xml.XmlElement;
import javolution37.javolution.xml.XmlFormat;

import com.jstatcom.model.control.SymbolTableTreeNode;
import com.jstatcom.model.control.SymbolTree;
import com.jstatcom.model.control.SymbolTreeNode;

/**
 * This class contains a sorted collection of Symbol objects that
 * can easily be accessed.
 * 

* This class is thread-save. All access to mutable data is synchronized. * * @author Markus Kraetzig */ public class SymbolTable { private SymbolTableTreeNode symbolTableNode = null; private final String name; private final SymbolTable parentTable; /** * Identifier of this table (always upper case). */ public final String NAME; private final List symbolList = new ArrayList(); /** * XML format field for (de)serialization. */ public static final XmlFormat SymbolTable_XML = new XmlFormat( SymbolTable.class) { public void format(SymbolTable data, XmlElement xml) { xml.setAttribute("name", data.name); synchronized (data) { for (Iterator it = data.symbolList.iterator(); it.hasNext();) { Object o = it.next(); xml.add(o); } } } public SymbolTable parse(XmlElement xml) { String name = xml.getAttribute("name") + ""; SymbolTable sTable = new SymbolTable(name, false); while (xml.hasNext()) sTable.setSymbol((Symbol) xml.getNext()); return sTable; } }; /** * Creates an empty symbol table with name, and no parent. * It will be added to the symbol tree. * * @param name * the identifier of that SymbolTable */ public SymbolTable(String name) { this(name, null, true); } /** * Creates an empty symbol table with name, and no parent. * It can optionally be added to the symbol tree. * * @param name * the identifier of that SymbolTable * @param addToTree * if true then this table will be added to the * symbol tree, otherwise it will not be added */ public SymbolTable(String name, boolean addToTree) { this(name, null, addToTree); } /** * Creates an empty symbol table with name and a parent * table. It will be added to the symbol tree. * * @param name * the identifier of that SymbolTable * @param parentTable * the parent table, will be parent node in symbol tree */ public SymbolTable(String name, SymbolTable parentTable) { this(name, parentTable, true); } /** * Creates an empty symbol table with name and a parent * table. It can optionally be added to the symbol tree. * * @param name * the identifier of that SymbolTable * @param parentTable * the parent table, will be parent node in symbol tree * @param addToTree * if true then this table will be added to the * symbol tree, otherwise it will not be added */ public SymbolTable(String name, SymbolTable parentTable, boolean addToTree) { super(); if (name == null) name = "null"; this.parentTable = parentTable; this.name = name; this.NAME = name.toUpperCase(); if (addToTree) SymbolTree.getInstance().addSymbolTable(this); } /** * Adds an array of JSCData objects to the collection. * * @param data * the JSCData array to put * * @throws IllegalArgumentException * if (data == null)
* or if a symbol with data[i].getName() but with * a different type already exists */ public final void set(JSCData[] data) { if (data == null) throw new IllegalArgumentException("Argument was null."); if (data.length == 0) // Fast fail. return; // not synchronized because in set the events are fired for (int i = 0; i < data.length; i++) set(data[i]); } /** * Adds a new JSCData object to the collection. If a symbol * with the same name as data is already contained, * data will be set for that symbol. If no symbol of * data.getName() is contained, then a new symbol will be * created and added. * * @param data * the JSCData object to put * * @throws IllegalArgumentException * if (data == null)
* or if a symbol with data.getName() but with a * different type already exists */ public final void set(JSCData data) { if (data == null) throw new IllegalArgumentException("Argument was null."); Symbol toPut = new Symbol(data); Symbol symbol = null; boolean contains = false; synchronized (this) { if (contains = symbolList.contains(toPut)) { symbol = symbolList.get(symbolList.indexOf(toPut)); } } // set symbol outside synched block, because events might be fired // and listeners might need access to this table from a new thread if (contains) symbol.setJSCData(data); else setSymbol(toPut); } /** * Adds a new JSCData object to the collection. If a symbol * with the name symbolName is already contained, * data will be set for that symbol. If no symbol of * symbolName is contained, then a new symbol will be created * and added. * * @param symbolName * the name of the symbol to be created * @param data * the JSCData object to put * * @throws IllegalArgumentException * if (data == null || symbolName == null)
* or if a symbol with symbolName but with a * different type already exists
* or if symbolName is not a valid name as * defined in JSCConstants * */ public final void set(String symbolName, JSCData data) { if (data == null || symbolName == null) throw new IllegalArgumentException("Argument was null."); Symbol toPut = new Symbol(symbolName, data); synchronized (this) { if (symbolList.contains(toPut)) { Symbol symbol = symbolList.get(symbolList.indexOf(toPut)); symbol.setJSCData(data); } else setSymbol(toPut); } } /** * Puts a new symbol into the collection. The symbol is put on a position * that keeps the underlying collection alphabetically sorted without * respect to the case. If a symbol with the same name is alread contained, * then the data and listeners are copied to the existing symbol. * * @param symbol * the Symbol object to put * @throws IllegalArgumentException * if (symbol == null) */ public final void setSymbol(Symbol symbol) { if (symbol == null) throw new IllegalArgumentException("Argument was null."); synchronized (this) { if (symbolList.contains(symbol)) { Symbol.copySymbol(symbol, getSymbol(symbol.NAME)); return; } boolean inserted = false; for (int i = 0; i < symbolList.size(); i++) { if (symbolList.get(i).compareTo(symbol) > 0) { symbolList.add(i, symbol); inserted = true; break; } } if (!inserted) symbolList.add(symbol); // update the symbol table node if (symbolTableNode != null) symbolTableNode.addSymbol(symbol); } } /** * Removes all elements from this collection. *

* Usage Note:
* Removing a symbol from the SymbolTable does not affect any * SymbolListeners attached to that symbol. They are still * notified about changes in the removed symbol and not informed if a new * symbol with name is added. You might consider calling * removeAllSymbolListeners before that symbol is removed if * this is not what you want. */ public final synchronized void clear() { symbolList.clear(); if (symbolTableNode != null) symbolTableNode.clear(); } /** * Gets whether this symbol table contains a symbol with name * symbolName. * * @param symbolName * the name of the symbol to check for * @return true if contained, false otherwise */ public final synchronized boolean contains(String symbolName) { return symbolList.contains(new Symbol(new JSCTypeDef(symbolName, JSCTypes.NUMBER))); } /** * Gets the symbol with the given name or null * if none is contained with the given name. *

* Usage Note:
* Pleae notice the difference to the typesave get method. * This does not create a new symbol if it is not contained yet, because it * is not known, which type the new symbol should have. * * @param name * the identifier for the symbol * @return the symbol that was stored or a new empty symbol * * @throws IllegalArgumentException * if (name == null) */ public final Symbol getSymbol(String name) { if (name == null) throw new IllegalArgumentException("Argument was null."); Symbol symbol = new Symbol(new JSCTypeDef(name, JSCTypes.NUMBER)); // type is arbitrary synchronized (this) { if (symbolList.contains(symbol)) return symbolList.get(symbolList.indexOf(symbol)); // Not contained, null. return null; } } /** * Gets the symbol with def.name. If a symbol with that name * does not yet exist an empty symbol of the given type is created, added * and returned. * * @param def * the type definition for the symbol * @return the symbol that was stored or a new empty symbol * * @throws IllegalArgumentException * if (def == null) * @throws RuntimeException * if the retrieved symbol has a different type */ public final Symbol get(JSCTypeDef def) { if (def == null) throw new IllegalArgumentException("Argument was null."); Symbol symbol = new Symbol(def); synchronized (this) { if (symbolList.contains(symbol)) { symbol = symbolList.get(symbolList.indexOf(symbol)); if (symbol.type != def.type) throw new RuntimeException("The symbol \"" + def.name + "\" to be retrieved is of type " + symbol.type + " instead of " + def.type + "."); if (symbol.getDescription().length() == 0 && def.description.length() > 0) { symbol.setDescription(def.description); } return symbol; } // Not contained, new empty symbol is created. setSymbol(symbol); return symbol; } } /** * Gets the JSCData stored in the symbol with given * def.name. If a symbol with that name does not yet exist, * a symbol of the given type is created, added and an empty * JSCData object is returned. *

* Instead of calling this method, one of the convenience methods for * special JSCDataTypes can be used. * * @param def * the type definition for the symbol * @return the JSCData that was stored or a new empty data * object * * @throws IllegalArgumentException * if (def == null) * @throws RuntimeException * if the retrieved symbol has a different type */ public final synchronized JSCData getJSCData(JSCTypeDef def) { if (def == null) throw new IllegalArgumentException("Typedef was null."); return get(def).getJSCData(); } /** * Gets the name of this symbol table. * * @return the name */ public final String getName() { return name; } /** * Gets whether this list is empty. * * @return true if no elements are contained, * false otherwise */ public final synchronized boolean isEmpty() { return symbolList.size() == 0; } /** * Gets an Iterator for the symbols stored in this * collection. The iterator is guaranteed to walk through all stored symbols * in alphabetical order, starting with the lowest. *

* Usage Note:
* Clients must deal with synchronization while traversing this iterator, * because this can change during that operation. The * simplest way is to lock this symbol table during the entire traversal. If * clients can make sure that no other threads access this symbol table * during traversal, locking can be ommitted. * * @return an Iterator for all symbols */ public final Iterator iterator() { return symbolList.iterator(); } /** * Removes the symbol with name from this collection. The * case of the name makes no difference. *

* Usage Note:
* Removing a symbol from the SymbolTable does not affect any * SymbolListeners attached to that symbol. They are still * notified about changes in the removed symbol and not informed if a new * symbol with name is added. You might consider calling * removeAllSymbolListeners before that symbol is removed if * this is not what you want. * * @param name * the identifier for the symbol to remove. * @return true if this list contained the specified element * @throws IllegalArgumentException * if (name == null) * @see com.jstatcom.model.Symbol#equals */ public final boolean remove(String name) { if (name == null) throw new IllegalArgumentException("Argument was null."); synchronized (this) { // Type makes no difference here, just to find the symbol. boolean ret = symbolList.remove(new Symbol(new JSCTypeDef(name, JSCTypes.DATE))); // update the symbol table node if (ret && symbolTableNode != null) symbolTableNode.removeSymbol(name); return ret; } } /** * Gets the number of stored symbols in this collection. * * @return the size */ public final synchronized int size() { return symbolList.size(); } /** * Returns a string that represents the value of this object. * * @return a string representation of the receiver */ public final synchronized String toString() { return super.toString() + ",name=" + name + ",values=" + symbolList; } /** * Gets the symbol table that is referenced as a parent of this table. * * @return a symbol table */ public final SymbolTable getParentTable() { return parentTable; } /** * Sets the symbol table node that represents this table in a * SymbolTree. * * @param node * to representthis table */ public void setSymbolTableNode(SymbolTableTreeNode node) { symbolTableNode = node; symbolTableNode.clear(); // put all symbols into node for (Iterator iter = iterator(); iter.hasNext();) { Symbol element = (Symbol) iter.next(); symbolTableNode.add(new SymbolTreeNode(element)); } } /** * Gets the symbol table node that represents this table in a * SymbolTree. * * @return node representing this table */ public SymbolTableTreeNode getSymbolTableNode() { return symbolTableNode; } /** * Sets the contents of table to this symbol table by * applying the following rules: *

*

* This operation does not change the argument symbol table, but by copying * references its symbols might be affected by operations on this table * afterwards. *

* This method does not copy any listeners from table to * this. Listeners of this table are not changed by this * operation, except that they are notified via events in case of relevant * changes. * * @param table * the symbol table from which symbol are copied to this table * @throws IllegalArgumentException * if (table == null) */ public void setSymbolTable(SymbolTable table) { if (table == null) throw new IllegalArgumentException("Argument was null."); synchronized (this) { synchronized (table) { for (Iterator iter = table.iterator(); iter.hasNext();) { Symbol element = (Symbol) iter.next(); get(new JSCTypeDef(element.NAME, element.type)).setJSCData( element.getJSCData()); } } } } }

Ads help maintain this website.