001: package jsx3.xml;
002:
003: import java.util.Iterator;
004: import java.util.LinkedList;
005:
006: /**
007: * A Node is a container of Records.
008: * In use, Node has a parent Node and is part of a CdfDocument.
009: * @author Joe Walker [joe at getahead dot ltd dot uk]
010: */
011: public abstract class Node implements Iterable<Record> {
012: /**
013: * You should not be directly creating Nodes
014: */
015: protected Node(String id) {
016: this .id = id;
017: }
018:
019: /**
020: * You should not be directly creating Nodes
021: */
022: protected Node() {
023: }
024:
025: /**
026: * A <code>NodeList</code> that contains all children of this node. If
027: * there are no children, this is a <code>NodeList</code> containing no
028: * nodes.
029: */
030: public Iterator<Record> iterator() {
031: return children.iterator();
032: }
033:
034: /**
035: * Inserts the node <code>newChild</code> at the given <code>position</code>
036: * @param position The position at which to insert the new Record
037: * @param newRecord The node to insert.
038: */
039: public void insertRecord(int position, Record newRecord) {
040: children.add(position, newRecord);
041:
042: if (newRecord.getOwnerDocument() != null) {
043: newRecord.getParentNode().removeRecord(newRecord);
044: }
045:
046: newRecord.joinDocument(this );
047: }
048:
049: /**
050: * Replaces the child Record <code>oldRecord</code> with <code>newRecord</code>
051: * in the list of children, and returns the <code>oldRecord</code> Record.
052: * If the <code>newRecord</code> is already in the tree, it is first removed.
053: * @param newRecord The new Record to put in the child list.
054: * @param oldRecord The Record being replaced in the list.
055: */
056: public void replaceRecord(Record newRecord, Record oldRecord) {
057: int index = children.indexOf(oldRecord);
058: if (index == -1) {
059: appendRecord(newRecord);
060: } else {
061: removeRecord(oldRecord);
062: insertRecord(index, oldRecord);
063: }
064: }
065:
066: /**
067: * Removes the child Record indicated by <code>oldRecord</code> from the list
068: * of children, and returns it.
069: * @param oldRecord The Record being removed.
070: */
071: public void removeRecord(Record oldRecord) {
072: boolean removed = children.remove(oldRecord);
073: if (removed) {
074: oldRecord.leaveDocument();
075: }
076: }
077:
078: /**
079: * Adds the Record <code>newRecord</code> to the end of the list of children
080: * of this Record. If the <code>newRecord</code> is already in the tree, it
081: * is first removed.
082: * @param newRecord The Record to add.
083: */
084: public void appendRecord(Record newRecord) {
085: if (newRecord.getOwnerDocument() != null) {
086: newRecord.getParentNode().removeRecord(newRecord);
087: }
088:
089: newRecord.joinDocument(this );
090: children.add(newRecord);
091: }
092:
093: /**
094: * The parent of this node. The
095: * If a node has just been created and not yet added to the
096: * tree, or if it has been removed from the tree, this is
097: * <code>null</code>.
098: */
099: public Node getParentNode() {
100: return parentNode;
101: }
102:
103: /**
104: * The {@link CdfDocument} object associated with this node.
105: * @return This Records parent Document
106: */
107: public CdfDocument getOwnerDocument() {
108: return document;
109: }
110:
111: /**
112: * Remove all the nodes from this node
113: */
114: public void clear() {
115: for (Record record : children) {
116: removeRecord(record);
117: }
118: }
119:
120: /**
121: * Internal setter for the parent node.
122: * @param parent The new parent Node
123: */
124: protected void joinDocument(Node parent) {
125: this .document = parent.getOwnerDocument();
126: this .parentNode = parent;
127: }
128:
129: /**
130: * Internal way to disconnect a Node from a Document
131: */
132: protected void leaveDocument() {
133: this .document = null;
134: this .parentNode = null;
135: }
136:
137: /**
138: * @return the id
139: */
140: public String getId() {
141: return id;
142: }
143:
144: /**
145: * @param id the id to set
146: */
147: public void setId(String id) {
148: this .id = id;
149: }
150:
151: /* (non-Javadoc)
152: * @see java.lang.Object#equals(java.lang.Object)
153: */
154: @Override
155: public boolean equals(Object obj) {
156: if (obj == null) {
157: return false;
158: }
159:
160: if (this == obj) {
161: return true;
162: }
163:
164: if (!(obj instanceof Node)) {
165: return false;
166: }
167:
168: return super .equals(obj);
169: }
170:
171: /* (non-Javadoc)
172: * @see java.lang.Object#hashCode()
173: */
174: @Override
175: public int hashCode() {
176: return 37829 + id.hashCode();
177: }
178:
179: /**
180: * Local utility to create an indentation string
181: * @param depth the number of indents that have been done
182: * @return A string of depth <code>depth * 2</code>
183: */
184: protected static final String indent(int depth) {
185: StringBuilder reply = new StringBuilder(depth * 2);
186: for (int i = 0; i < depth; i++) {
187: reply.append(" ");
188: }
189: return reply.toString();
190: }
191:
192: /**
193: * The JSXID of this node
194: */
195: protected String id;
196:
197: /**
198: * Our parent record
199: */
200: protected Node parentNode;
201:
202: /**
203: * The CdfDocument that we are a part of
204: */
205: protected CdfDocument document;
206:
207: /**
208: * The records that we contain
209: */
210: protected LinkedList<Record> children = new LinkedList<Record>();
211: }
|