// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library; import java.io.IOException; import java.io.Serializable; /** * The abstract base class for all element and attribute classes in the SIF * Data Objects library.<p> * * Agent developers do not generally work with this class directly.<p> * * <b>ElementDef</b><p> * * Each Element instance is associated with an ElementDef that both identifies * and describes the element or attribute encapsulated by this class. ElementDef * constants are defined by the <code>SIFDTD</code> class for each element and * attribute defined in the SIF Specification. These objects provide * information about how to render elements in a version-dependent way, * including the tag name and sequence number (which may vary from one version * of SIF to the next), the SDO implementation class name, the earliest version * of SIF the element or attribute appeared in, and the latest version of SIF * that supports the element or attribute.<p> * * An ElementDef must be provided to the constructor. * * @author Eric Petersen * @version ADK 1.0 */ public abstract class Element implements Serializable, Cloneable { /** * The default java serialization version for ADK SDO classes */ public static final int CURRENT_SERIALIZE_VERSION = 2; /** * State flag indicating this Element has a dirty value. This state is * enabled at the time of constuction (i.e. all elements are dirty by * default) and must be explicitly disabled by an agent as necessary. */ private static final byte DIRTY = 0x01; /** * State flag indicating this Element has an empty value. This state is * disabled at the time of construction, but may be set or cleared by * methods that set this element's value. */ private static final byte EMPTY = 0x02; // /** // * State flag indicating that this Element has a "Deleted='True'" attribute. // * The "Deleted='True'" attribute is a flag that has a meaning only for repeatable // * elements // */ // protected static final byte DELETED = 0x04; /** * Option flag indicating XML Encoding of character entities should not * be performed on this Element's text content when rendered by the ADK. */ private static final byte OPT_DO_NOT_ENCODE = 0x04; /** * Identifies this field by its ElementDef constant defined in SIFDTD */ protected transient ElementDef fElementDef; /** * State flags keep track of whether this Element is changed or has an * empty value. By default the ADK does not track the state of an Element * when the #setTextValue method is called to change its value. The * programmer must explicitly call the #setChanged and #setEmpty methods * to mark an Element as changed or empty. */ protected byte fFlags; /** * The parent Element, or null if there is no parent */ protected Element fParent; /** * Constructor * @param def The metadata that describes this Element */ public Element( ElementDef def ) { if( def == null ) throw new RuntimeException( "SIF " + ADK.getSIFVersion() + " does not support this element or attribute, or the required SDO library is not loaded (" + getClass().toString() + ")" ); fElementDef = def; fFlags = DIRTY; } /** * Gets the metadata for this Element * @return an ElementDef that describes this Element */ public ElementDef getElementDef() { return fElementDef; } /** * Sets the metadata for this Element.<p> * * Note this method should not generally be called by agents because the * ElementDef metadata is established in the constructor. It is provided * in order to support the dynamic creation of Element instances by clients * that do not (or cannot) use reflection to call the default constructor. * These clients can call the <code>Class.newInstance</code> method followed * by <code>setElementDef</code> to construct an Element dynamically.<p> * * @param def An ElementDef instance that describes this Element */ public void setElementDef( ElementDef def ) { fElementDef = def; } /** * Gets the text value of this element if applicable. The * default format of the text value is the SIF 1.x format<p> * To change the format used for Text values on elements, set the * {@link ADK#setTextFormatter(SIFFormatter)} property * @return The text value of this element */ public abstract String getTextValue(); /** * Sets the text value of this element if applicable. The * text value will be parsed into the native datatype of the * the element. <p> * The formatter used for parsing by default is * the SIF 1.x formatter, which means this value must be able * to be parsed using SIF 1.x formatting rules. * To change the format used for Text values on elements, set the * {@link ADK#setTextFormatter(SIFFormatter)} property * @param value The text value of this element */ public abstract void setTextValue( String value ); /** * Sets the SIF strongly-typed value of this element * @param value The SIF Value to set */ public abstract void setSIFValue( SIFSimpleType value ); /** * Gets the SIF strongly-typed value of this element * @return The value of this element as a <c>SIFSimpleType</c> */ public abstract SIFSimpleType getSIFValue(); /** * Sets this DataObject and each of its children to the dirty state. An * object in the dirty state will not be written to an XML stream when a * SIF message is rendered. */ public void setChanged() { setChanged(true); } /** * Sets this DataObject and each of its children to the specified dirty * state. An object in the dirty state will not be written to an XML stream * when a SIF message is rendered. * * @param changed true to set the dirty state, false to clear it */ public void setChanged( boolean changed ) { if( changed ) { fFlags |= DIRTY; if( fParent != null ){ fParent.setChildChanged(); } } else { fFlags &= ~DIRTY; } } /** * Called by children when they are set to a "changed" state. The * parent of any child element that is changed should also be marked as * changed (but not any other children of the parent). */ protected void setChildChanged(){ // don't want to recurse children. This just a call // to recurse up the chain if( !isChanged() ){ fFlags |= DIRTY; if( fParent != null ){ fParent.setChildChanged(); } } } /** * Sets this DataObject and each of its children to the empty state. An * object in the empty state will be written to an XML stream as an empty * element with no attributes and no child elements. */ public void setEmpty() { setEmpty(true); } /** * Sets this DataObject and each of its children to the specified empty * state. An object in the empty state will be written to an XML stream as * an empty element with no attributes and no child elements. * * @param empty true to set the empty state, false to clear it */ public void setEmpty( boolean empty ) { if( empty ) { fFlags |= EMPTY; } else { fFlags &= ~EMPTY; } } /** * Determines if this object is in the changed state. * @return true if this object has been marked changed. The return value * assumes all children are in the same state. */ public boolean isChanged() { return ( fFlags & DIRTY ) != 0; } /** * Determines if this object is in the empty state * @return true if this object has explicitly been marked empty. The return * value assumes all children are in the same state. */ public boolean isEmpty() { return ( fFlags & EMPTY ) != 0; } /** * Determines if automatic XML Encoding of character entities should be * performed on this element when rendered by the ADK. By default, all * elements and attributes are encoded. Use the #setDoNotEncode method to * turn off automatic encoding for an element if you will be assigning * XML content to its text value (e.g. if you are using SIF_ExtendedElement * to exchange raw XML content with another agent). * * @return <code>true</code> if automatic XML Encoding is disabled for this * element; <code>false</code> if enabled (the default) */ public boolean isDoNotEncode() { if( ( fFlags & OPT_DO_NOT_ENCODE ) != 0 ) return true; if( fElementDef != null ) { return fElementDef.isDoNotEncode(); } return false; } /** * Determines if automatic XML Encoding of character entities should be * performed on this element when rendered by the ADK. By default, all * elements and attributes are encoded. Use this method to turn off * automatic encoding for an element if you will be assigning XML content * to its text value (e.g. if you are using SIF_ExtendedElement * to exchange raw XML content with another agent). * * @param option <code>true</code> if automatic XML Encoding is disabled * for this element; <code>false</code> if enabled (the default) */ public void setDoNotEncode( boolean option ) { if( option ) { fFlags |= OPT_DO_NOT_ENCODE; } else { fFlags &= ~OPT_DO_NOT_ENCODE; } } /** * Compare the text value of this Element to another Element<p> * * @param target The Element to be compared * * @return the value <code>0</code> if the argument's text value is * lexicographically equal to this Element's text value; a value less * than <code>0</code> if the argument's value is lexicographically * greater than this Element's text value; and a value greater than * <code>0</code> if the argument's text value is lexicographically * less than this Element's text value. If one Element's text value is * null and the others is not, a negative value is returned. */ public int compareTo( Element target ) { String s1 = getTextValue(); String s2 = target.getTextValue(); if( s1 != null && s2 != null ) return s1.compareTo(s2); if( s1 == null && s2 == null ) return 0; return -1; } private void writeObject( java.io.ObjectOutputStream out ) throws IOException { out.defaultWriteObject(); // // Write serialize version // out.write( CURRENT_SERIALIZE_VERSION ); } private void readObject( java.io.ObjectInputStream in ) throws IOException, ClassNotFoundException { in.defaultReadObject(); // // Read serialize version // int version = in.read(); // NOTE: This type of version checking is also done by the Java serialization // infrastructure. We decided to keep our check as well because we may decide // to implement our own versioning strategy. For now, however, just throw an exception // if the versions don't match. if( version != Element.CURRENT_SERIALIZE_VERSION ){ throw new IOException("Cannot deserialize Element with version signature ("+version+")"); } } /** * Returns the value of <code>getTextValue</code> * @return The text value of this element if applicable */ public String toString() { String retval = ""; if (getTextValue() != null) retval = getTextValue(); else if (fElementDef != null) retval = fElementDef.tag(ADK.getSIFVersion()); return retval; // return getTextValue(); } /** * Gets the parent of this Element.<p> * @return this Element's parent or <code>null</code> if it has none * @see #setParent */ public Element getParent() { return fParent; } /** * Sets the parent of this Element.<p> * @param parent the new parent Element * @see #getParent */ public void setParent( Element parent ) { fParent = parent; } /** * Enumerating the ancestry of this object to return the root Element<p> * @return the root Element that is a parent of this Element */ public Element getRoot() { if( fParent == null ) return this; Element p = fParent; while( p.fParent != null ) p = p.fParent; return p; } }