/////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1998, California Institute of Technology.
// ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged.
//
// Please read the full copyright notice in the file COPYRIGHT
// in this directory.
//
// Author: Jake Hamby, NASA/Jet Propulsion Laboratory
// Jake.Hamby@jpl.nasa.gov
/////////////////////////////////////////////////////////////////////////////
package dods.dap;
import java.util.Enumeration;
import dods.util.SortedTable;
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>.
*
* @version $Revision: 1.3 $
* @author jehamby
* @see DAS
* @see Attribute
*/
public class AttributeTable implements Cloneable {
private static final boolean _Debug = false;
/** A table of Attributes with their names as a key */
private SortedTable attr;
/** What's the name of this table? */
private String name;
/** Create a new empty <code>AttributeTable</code>.
@deprecated */
public AttributeTable() {
attr = new SortedTable();
}
/** Create a new empty <code>AttributeTable</code>. */
public AttributeTable(String name) {
this.name = name;
attr = new SortedTable();
}
/**
* Returns a clone of this <code>AttributeTable</code>. A deep copy is
* performed on all <code>Attribute</code> and <code>AttributeTable</code>
* objects inside the <code>AttributeTable</code>.
*
* @return a clone of this <code>AttributeTable</code>.
*/
public Object clone() {
try {
AttributeTable at = (AttributeTable)super.clone();
at.name = name;
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, element.clone());
}
return at;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/** Returns the name of this AttributeTable. */
public final String getName() {
return name;
}
/**
* 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 name 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 name) {
return (Attribute)attr.get(name);
}
/**
* 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 name 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?
* @exception AttributeExistsException thrown if an Attribute with the same
* name, but a different type was previously defined.
* @exception AttributeBadValueException thrown if the value is not a legal
* member of type
* @see AttributeTable#appendContainer(String)
*/
public final void appendAttribute(String name, int type, String value,
boolean check)
throws AttributeExistsException, AttributeBadValueException {
Attribute a = (Attribute)attr.get(name);
if (a != null && (type != a.getType())) {
// type mismatch error
throw new AttributeExistsException("`" + name
+ "' previously defined with a different type.");
} else if (a != null) {
a.appendValue(value, check);
} else {
a = new Attribute(type, name, value, check);
attr.put(name, 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 name 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.
* @exception AttributeExistsException thrown if an Attribute with the same
* name, but a different type was previously defined.
* @exception AttributeBadValueException thrown if the value is not a legal
* member of type
* @see AttributeTable#appendContainer(String)
*/
public final void appendAttribute(String name, int type, String value)
throws AttributeExistsException, AttributeBadValueException {
appendAttribute(name, type, value, true);
}
/**
* Create and append an attribute container to the table.
* A container is another <code>AttributeTable</code> object.
*
* @param name 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 name) {
// return null if name already exists
if (attr.get(name) != null)
return null;
AttributeTable at = new AttributeTable(name);
Attribute a = new Attribute(name, at);
attr.put(name, a);
return at;
}
/**
* Add an alias to the current table.
*
* @param alias The alias to insert into the attribute table.
* @param name The name of the already-existing attribute to which
* the alias will refer.
* @exception NoSuchAttributeException thrown if the existing attribute
* could not be found.
* @exception AttributeExistsException thrown if the new alias has the same
* name as an existing attribute.
*/
public final void addAlias(String alias, String name)
throws NoSuchAttributeException, AttributeExistsException {
// return false if alias already exists.
if (attr.get(alias) != null)
throw new AttributeExistsException("Could not alias `" + name +
"' and `" + alias + "'.");
// Make sure name exists.
Attribute a = (Attribute)attr.get(name);
if (a == null)
throw new NoSuchAttributeException("Could not alias `" + name +
"' and `" + alias + "'.");
Attribute newAttr = new Attribute(name, a);
attr.put(alias, newAttr);
}
/**
* Delete the attribute named <code>name</code>.
*
* @param name The name of the attribute to delete. This can be an
* attribute of any type, including containers.
*/
public final void delAttribute(String name) {
attr.remove(name);
}
/**
* 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 name 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 name, int i) {
if (i == -1) { // delete the whole attribute
attr.remove(name);
}
else {
Attribute a = (Attribute)attr.get(name);
if (a != null) {
if (a.isContainer()) {
attr.remove(name); // 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) System.out.println("Entered AttributeTable.print()");
for (Enumeration e = getNames(); e.hasMoreElements() ;) {
String name = (String)e.nextElement();
Attribute a = getAttribute(name);
if (a.isAlias()) {
if (_Debug) System.out.println(" Attribute \"" + name + "\" is an Alias.");
os.println(pad + "Alias " + name + " " + a.getAliasedTo() + ";");
}
else {
if (a.isContainer()) {
if (_Debug) System.out.println(" Attribute \"" + name + "\" is a Container.");
os.println(pad + name + " {");
((AttributeTable)a.getContainer()).print(os, pad + " ");
os.println(pad + "}");
}
else {
if (_Debug) System.out.println(" Printing Attribute \"" + name + "\".");
os.print(pad + a.getTypeString() + " " + name + " ");
Enumeration es = a.getValues();
String val = (String)es.nextElement(); // get first element
while(es.hasMoreElements()) { // lookahead one element
os.print(val + ", ");
val = (String)es.nextElement();
}
os.println(val + ";"); // print last element
}
}
}
os.flush();
if (_Debug) System.out.println("Leaving AttributeTable.print()");
}
/**
* 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))), 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(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, " ");
}
}
// $Log: not supported by cvs2svn $
// 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.
//