///////////////////////////////////////////////////////////////////////////// // This file is part of the "Java-DAP" project, a Java implementation // of the OPeNDAP Data Access Protocol. // // Copyright (c) 2010, OPeNDAP, Inc. // Copyright (c) 2002,2003 OPeNDAP, Inc. // // Author: James Gallagher <jgallagher@opendap.org> // // All rights reserved. // // Redistribution and use in source and binary forms, // with or without modification, are permitted provided // that the following conditions are met: // // - Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // - Neither the name of the OPeNDAP nor the names of its contributors may // be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ///////////////////////////////////////////////////////////////////////////// package opendap.dap; import java.util.Enumeration; import opendap.dap.parsers.DDSXMLParser; import opendap.util.SortedTable; import opendap.util.Debug; import java.io.*; /** * An <code>AttributeTable</code> stores a set of names and, for each name, * an <code>Attribute</code> object. For more information on the types of * data which can be stored in an attribute, including aliases and other * <code>AttributeTable</code> objects, see the documentation for * <code>Attribute</code>. * <p/> * The attribute tables have a standard printed representation. There is a * <code>print</code> method for writing this form and a <code>parse</code> * method for reading the printed form. * <p/> * An <code>AttributeTable</code>'s print representation might look like: * <pre> * String long_name "Weekly Means of Sea Surface Temperature"; * </pre> * or * <pre> * actual_range { * Float64 min -1.8; * Float64 max 35.09; * } * </pre> * or * <pre> * String Investigators "Cornillon", "Fleirl", "Watts"; * </pre> * or * <pre> * Alias New_Attribute Old_Attribute; * </pre> * Here, <em>long_name</em> and <em>Investigators</em> are * simple attributes, <em>actual_range</em> is a container attribute, and * <em>New_Attribute</em> is an alias pointing to <em>Old_Attribute</em>. * * @author jehamby * @version $Revision: 15901 $ * @see DAS * @see Attribute * * Modified 1/9/2011 Dennis Heimbigner * - Make subclass of BaseType for uniformity */ public class AttributeTable extends DAPNode { /** * A table of Attributes with their names as a key */ private SortedTable _attr; /** * Create a new empty <code>AttributeTable</code>. * * @deprecated Use constructor that takes the name of the table. */ public AttributeTable() { _attr = new SortedTable(); } /** * Create a new empty <code>AttributeTable</code>. */ public AttributeTable(String clearname) { super(clearname); _attr = new SortedTable(); } /** * * @return the # of contained attributes */ public int size() { return (_attr == null ? 0 : _attr.size()); } /** * Returns an <code>Enumeration</code> of the attribute names in this * <code>AttributeTable</code>. * Use the <code>getAttribute</code> method to get the * <code>Attribute</code> for a given name. * * @return an <code>Enumeration</code> of <code>String</code>. * @see AttributeTable#getAttribute(String) */ public final Enumeration getNames() { return _attr.keys(); } /** * Returns the <code>Attribute</code> which matches name. * * @param clearname the name of the <code>Attribute</code> to return. * @return the <code>Attribute</code> with the specified name, or null * if there is no matching <code>Attribute</code>. * @see Attribute */ public final Attribute getAttribute(String clearname) { //throws NoSuchAttributeException { Attribute a = (Attribute) _attr.get(clearname); return (a); } /** * Returns the <code>Attribute</code> which matches name. * * @param clearname the name of the <code>Attribute</code> to return. * @return True if an Attribute with named 'name' exists, False otherwise. * @see Attribute */ public final boolean hasAttribute(String clearname) { Attribute a = (Attribute) _attr.get(clearname); if (a == null) { return (false); } return (true); } /** * Adds an attribute to the table. If the given name already * refers to an attribute, and the attribute has a vector value, * the given value is appended to the attribute vector. Calling * this function repeatedly is the way to create an attribute * vector. * <p/> * The function throws an exception if the attribute is a * container, or if the type of the input value does not match the * existing attribute's type and the <code>check</code> parameter * is true. Use the <code>appendContainer</code> method to add container * attributes. * * @param clearname The name of the attribute to add or modify. * @param type The type code of the attribute to add or modify. * @param value The value to add to the attribute table. * @param check Check the validity of the attribute's value? * @throws AttributeExistsException thrown if an Attribute with the same * name, but a different type was previously defined. * @throws AttributeBadValueException thrown if the value is not a legal * member of type * @see AttributeTable#appendContainer(String) */ public final void appendAttribute(String clearname, int type, String value, boolean check) throws DASException { Attribute a = (Attribute) _attr.get(clearname); if (a != null && (type != a.getType())) { // type mismatch error throw new AttributeExistsException("The Attribute `" + clearname + "' was previously defined with a different type."); } else if (a != null) { a.appendValue(value, check); } else { a = new Attribute(type, clearname, value, check); _attr.put(clearname, a); } } /** * Adds an attribute to the table. If the given name already * refers to an attribute, and the attribute has a vector value, * the given value is appended to the attribute vector. Calling * this function repeatedly is the way to create an attribute * vector. * <p/> * The function throws an exception if the attribute is a * container, or if the type of the input value does not match the * existing attribute's type. Use the <code>appendContainer</code> * method to add container attributes. * * @param clearname The name of the attribute to add or modify. * @param type The type code of the attribute to add or modify. * @param value The value to add to the attribute table. * @throws AttributeExistsException thrown if an Attribute with the same * name, but a different type was previously defined. * @throws AttributeBadValueException thrown if the value is not a legal * member of type * @see AttributeTable#appendContainer(String) */ public final void appendAttribute(String clearname, int type, String value) throws DASException { appendAttribute(clearname, type, value, true); } /** * Create and append an attribute container to the table. * A container is another <code>AttributeTable</code> object. * * @param clearname the name of the container to add. * @return A pointer to the new <code>AttributeTable</code> object, or null * if a container by that name already exists. */ public final AttributeTable appendContainer(String clearname) { // return null if clearname already exists // FIXME! THIS SHOULD RETURN AN EXCEPTION! if (_attr.get(clearname) != null) return null; AttributeTable at = new AttributeTable(clearname); Attribute a = new Attribute(clearname, at); _attr.put(clearname, a); return at; } /** * Create and append an attribute container to the table. * A container is another <code>AttributeTable</code> object. * * @param clearname the name of the container to add. * if a container by that name already exists. */ public final void addContainer(String clearname, AttributeTable at) throws AttributeExistsException { // return null if name already exists if (_attr.get(clearname) != null) { throw new AttributeExistsException("The Attribute '" + clearname + "' already exists in the container '" + getEncodedName() + "'"); } Attribute a = new Attribute(clearname, at); _attr.put(clearname, a); } /** * Add an alias to the current table. * This method is used by the DAS parser to build Aliases * for the DAS. And the DDSXMLParser to add them to the DDX * <p/> * The new (9/26/02) DDS requires the use of <code> * addAlias(String, String, String)</code> and is the preffered * way of representing the DAS information. * * @param alias The alias to insert into the attribute table. * @param attributeName The normalized name of the attribute to which * the alias will refer. * @throws NoSuchAttributeException thrown if the existing attribute * could not be found. * @throws AttributeExistsException thrown if the new alias has the same * name as an existing attribute. */ public final void addAlias(String alias, String attributeName) throws NoSuchAttributeException, AttributeExistsException { // complain if alias name already exists in this AttributeTable. if (_attr.get(alias) != null) { throw new AttributeExistsException("Could not alias `" + alias + "' to `" + attributeName + "'. " + "It is a duplicat name in this AttributeTable"); } if (Debug.isSet("AttributTable")) { log.debug("Adding alias '" + alias + "' to AttributeTable '" + getClearName() + "'"); } Alias newAlias = new Alias(alias, attributeName); _attr.put(alias, newAlias); } /** * Delete the attribute named <code>name</code>. * * @param clearname The name of the attribute to delete. This can be an * attribute of any type, including containers. */ public final void delAttribute(String clearname) { _attr.remove(clearname); } /** * Delete the attribute named <code>name</code>. If the attribute has a * vector value, delete the <code>i</code>'th element of the vector. * * @param clearname The name of the attribute to delete. This can be an * attribute of any type, including containers. * @param i If the named attribute is a vector, and <code>i</code> is * non-negative, the <code>i</code>'th entry in the vector is deleted. * If <code>i</code> equals -1, the entire attribute is deleted. * @see AttributeTable#delAttribute(String) */ public final void delAttribute(String clearname, int i) throws DASException { if (i == -1) { // delete the whole attribute _attr.remove(clearname); } else { Attribute a = (Attribute) _attr.get(clearname); if (a != null) { if (a.isContainer()) { _attr.remove(clearname); // delete the entire container } else { a.deleteValueAt(i); } } } } /** * Print the attribute table on the given <code>PrintWriter</code>. * * @param os the <code>PrintWriter</code> to use for output. * @param pad the number of spaces to indent each line. */ public void print(PrintWriter os, String pad) { if (Debug.isSet("AttributTable")) os.println("Entered AttributeTable.print()"); os.println(pad + getEncodedName() + " {"); for (Enumeration e = getNames(); e.hasMoreElements();) { String name = (String) e.nextElement(); Attribute a = getAttribute(name); if (a != null) a.print(os, pad + " "); } os.println(pad + "}"); if (Debug.isSet("AttributTable")) os.println("Leaving AttributeTable.print()"); os.flush(); } /** * Print the attribute table on the given <code>OutputStream</code>. * * @param os the <code>OutputStream</code> to use for output. * @param pad the number of spaces to indent each line. */ public final void print(OutputStream os, String pad) { print(new PrintWriter(new BufferedWriter(new OutputStreamWriter(os,Util.UTF8))), pad); } /** * Print the attribute table on the given <code>PrintWriter</code> with * four spaces of indentation. * * @param os the <code>PrintWriter</code> to use for output. */ public final void print(PrintStream os) { print(os, ""); } /** * Print the attribute table on the given <code>PrintWriter</code> with * four spaces of indentation. * * @param os the <code>PrintWriter</code> to use for output. */ public final void print(PrintWriter os) { print(os, ""); } /** * Print the attribute table on the given <code>OutputStream</code> with * four spaces of indentation. * * @param os the <code>OutputStream</code> to use for output. */ public final void print(OutputStream os) { print(os, ""); } /** * * @param os * @opendap.ddx.experimental */ public void printXML(OutputStream os) { printXML(os, ""); } /** * * @param os * @param pad * @opendap.ddx.experimental */ public void printXML(OutputStream os, String pad) { PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os,Util.UTF8))); printXML(pw, pad); pw.flush(); } /** * * @param pw * @opendap.ddx.experimental */ public void printXML(PrintWriter pw) { printXML(pw, ""); } /** * * @param pw * @param pad * @opendap.ddx.experimental */ public void printXML(PrintWriter pw, String pad) { printXML(pw, pad, false); } /** * * @param pw * @param pad * @param constrained * @opendap.ddx.experimental */ public void printXML(PrintWriter pw, String pad, boolean constrained) { if (Debug.isSet("AttributTable")) pw.println("Entered AttributeTable.print()"); pw.println(pad + "<Attribute name=\"" + DDSXMLParser.normalizeToXML(getEncodedName()) + "\" type=\"Container\">"); Enumeration e = getNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); Attribute a = getAttribute(name); if (a != null) a.printXML(pw, pad + "\t", constrained); } pw.println(pad + "</Attribute>"); if (Debug.isSet("AttributTable")) pw.println("Leaving AttributeTable.print()"); pw.flush(); } /** * Returns a clone of this <code>AttributeTable</code>. * See DAPNode.cloneDag() * * @param map track previously cloned nodes * @return a clone of this <code>Attribute</code>. */ public DAPNode cloneDAG(CloneMap map) throws CloneNotSupportedException { AttributeTable at = (AttributeTable) super.cloneDAG(map); at._attr = new SortedTable(); for (int i = 0; i < _attr.size(); i++) { String key = (String) _attr.getKey(i); Attribute element = (Attribute) _attr.elementAt(i); // clone element (don't clone key because it's a read-only String) at._attr.put(key, (Attribute)cloneDAG(map,element)); } return at; } } // $Log: AttributeTable.java,v $ // Revision 1.3 2003/09/02 17:49:34 ndp // *** empty log message *** // // Revision 1.2 2003/09/02 15:06:25 ndp // *** empty log message *** // // Revision 1.1 2003/08/12 23:51:25 ndp // Mass check in to begin Java-OPeNDAP development work // // Revision 1.12 2011/01/09 Dennis Heimbigner // - Make subclass of BaseType for uniformity // // Revision 1.11 2003/04/07 22:12:32 jchamber // added serialization // // Revision 1.10 2003/02/12 16:41:15 ndp // *** empty log message *** // // Revision 1.9 2002/10/10 18:12:31 ndp // Fixed bugs in DDS.getDAS(), Updated testDataset and sqlDataset // // Revision 1.8 2002/10/08 21:59:18 ndp // Added XML functionality to the core. This includes the new DDS code (aka DDX) // for parsing XML representations of the dataset description ( that's a DDX) // Also BaseType has been modified to hold Attributes and methods added to DDS // to ingest DAS's (inorder to add Attributes to variables) and to get the DAS // object from the DDS. Geturl and DConnect hav been modified to provide client // access to this new set of functionalites. ndp 10/8/2002 // // Revision 1.7 2002/05/30 23:25:57 jimg // I added methods that provide a way to add attribues without the usual // type/value checking. See today's log in Attribute.java for more info. //