001: package org.osbl.client.wings.form;
002:
003: import org.osbl.client.wings.shell.Client;
004: import org.osbl.client.wings.form.ValidationStatus;
005: import org.osbl.persistence.model.TreeEntity;
006: import org.osbl.persistence.model.TreeNodeEntity;
007: import org.osbl.persistence.RevertCommand;
008: import org.conform.BeanMeta;
009: import org.conform.format.NoFormat;
010: import org.conform.format.Format;
011:
012: import javax.swing.tree.MutableTreeNode;
013: import javax.swing.tree.TreeNode;
014: import javax.swing.event.ChangeListener;
015: import javax.swing.event.ChangeEvent;
016: import java.util.*;
017:
018: public abstract class GenericObjectTreeNode<O> implements
019: MutableTreeNode, Comparable, ChangeListener {
020: private GenericObjectTreeModel model;
021: protected O object;
022: protected GenericObjectTreeNode parent;
023: protected List<GenericObjectTreeNode> children;
024: Format format;
025: private Map clientProperties;
026: private boolean trans;
027:
028: public GenericObjectTreeModel getModel() {
029: if (model == null)
030: model = getParent().getModel();
031: return model;
032: }
033:
034: public void setModel(GenericObjectTreeModel model) {
035: this .model = model;
036: }
037:
038: public O getObject() {
039: return object;
040: }
041:
042: public void setObject(O object) {
043: if (object == null)
044: throw new IllegalArgumentException("null not allowed");
045:
046: this .object = object;
047: }
048:
049: public void setUserObject(Object userObject) {
050: setObject((O) userObject);
051: }
052:
053: protected void maybeLoadChildren() {
054: if (children != null)
055: return;
056: if (trans)
057: children = Collections.EMPTY_LIST;
058: else
059: loadChildren();
060: }
061:
062: protected abstract void loadChildren();
063:
064: public boolean isTransient() {
065: return trans;
066: }
067:
068: public void setTransient(boolean trans) {
069: if (!trans && this .trans)
070: children = null;
071: this .trans = trans;
072: }
073:
074: public void removeFromParent() {
075: MutableTreeNode parent = getParent();
076: if (parent != null)
077: parent.remove(this );
078: model = null;
079: }
080:
081: public void remove(int index) {
082: maybeLoadChildren();
083: MutableTreeNode child = children.remove(index);
084: child.setParent(null);
085:
086: getModel().nodesWereRemoved(GenericObjectTreeNode.this ,
087: new int[] { index }, new Object[] { child });
088: }
089:
090: public void remove(MutableTreeNode child) {
091: maybeLoadChildren();
092: int index = children.indexOf(child);
093: children.remove(child);
094: child.setParent(null);
095:
096: getModel().nodesWereRemoved(GenericObjectTreeNode.this ,
097: new int[] { index }, new Object[] { child });
098: }
099:
100: public void setParent(MutableTreeNode newParent) {
101: parent = (GenericObjectTreeNode) newParent;
102: }
103:
104: public GenericObjectTreeNode getParent() {
105: return parent;
106: }
107:
108: public void insert(MutableTreeNode newChild, int index) {
109: maybeLoadChildren();
110: MutableTreeNode oldParent = (MutableTreeNode) newChild
111: .getParent();
112:
113: if (oldParent != null)
114: oldParent.remove(newChild);
115:
116: newChild.setParent(this );
117:
118: if (index == -1)
119: index = children.size();
120:
121: children.add(index, (GenericObjectTreeNode) newChild);
122: sortChildren();
123: index = children.indexOf(newChild);
124:
125: getModel().nodesWereInserted(GenericObjectTreeNode.this ,
126: new int[] { index });
127: }
128:
129: protected void sortChildren() {
130: Collections.sort(children);
131: }
132:
133: public int getChildCount() {
134: maybeLoadChildren();
135: return children.size();
136: }
137:
138: public abstract boolean getAllowsChildren();
139:
140: public abstract boolean isLeaf();
141:
142: public Enumeration<GenericObjectTreeNode> children() {
143: maybeLoadChildren();
144: return Collections.enumeration(children);
145: }
146:
147: public TreeNode getChildAt(int childIndex) {
148: maybeLoadChildren();
149: return children.get(childIndex);
150: }
151:
152: public int getIndex(TreeNode node) {
153: maybeLoadChildren();
154: return children.indexOf(node);
155: }
156:
157: public int compareTo(Object o) {
158: GenericObjectTreeNode node = (GenericObjectTreeNode) o;
159: if (isLeaf() && !node.isLeaf())
160: return 1;
161: else if (!isLeaf() && node.isLeaf())
162: return -1;
163:
164: if (object instanceof Comparable
165: && node.object instanceof Comparable)
166: return ((Comparable) object).compareTo(node.object);
167: else
168: return toString().compareTo(node.toString());
169: }
170:
171: public String toString() {
172: if (object == null) {
173: String type = Client.getInstance().getResourceProvider()
174: .getMessage(getObjectType().getName());
175: return Client.getInstance().getResourceProvider()
176: .getMessage("businessobject.newOfType", type);
177: } else
178: return format(object);
179: }
180:
181: protected String format(Object object) {
182: if (format == null) {
183: BeanMeta beanMeta = Client.getInstance()
184: .getBeanMetaProvider().getBeanMeta(getObjectType());
185: format = beanMeta.getFormat();
186: if (format == null)
187: format = new NoFormat();
188: }
189: return format.format(object);
190: }
191:
192: public boolean equals(Object o) {
193: if (this == o)
194: return true;
195: if (o == null || getClass() != o.getClass())
196: return false;
197:
198: GenericObjectTreeNode that = (GenericObjectTreeNode) o;
199:
200: if (object != null ? !object.equals(that.object)
201: : that.object != null)
202: return false;
203:
204: return true;
205: }
206:
207: public int hashCode() {
208: return (object != null ? object.hashCode() : 0);
209: }
210:
211: /**
212: * @return a small HashMap
213: * @see #putClientProperty
214: * @see #getClientProperty
215: */
216: private Map getClientProperties() {
217: if (clientProperties == null) {
218: clientProperties = new HashMap(2);
219: }
220: return clientProperties;
221: }
222:
223: /**
224: * Returns the value of the property with the specified key. Only
225: * properties added with <code>putClientProperty</code> will return
226: * a non-null value.
227: *
228: * @return the value of this property or null
229: * @see #putClientProperty
230: */
231: public final Object getClientProperty(Object key) {
232: if (clientProperties == null) {
233: return null;
234: } else {
235: return getClientProperties().get(key);
236: }
237: }
238:
239: /**
240: * Add an arbitrary key/value "client property" to this component.
241: * <p/>
242: * The <code>get/putClientProperty<code> methods provide access to
243: * a small per-instance hashtable. Callers can use get/putClientProperty
244: * to annotate components that were created by another module, e.g. a
245: * layout manager might store per child constraints this way. For example:
246: * <pre>
247: * componentA.putClientProperty("to the left of", componentB);
248: * </pre>
249: * <p/>
250: * If value is null this method will remove the property.
251: * Changes to client properties are reported with PropertyChange
252: * events. The name of the property (for the sake of PropertyChange
253: * events) is <code>key.toString()</code>.
254: * <p/>
255: * The clientProperty dictionary is not intended to support large
256: * scale extensions to SComponent nor should be it considered an
257: * alternative to subclassing when designing a new component.
258: *
259: * @see #getClientProperty
260: */
261: public final void putClientProperty(Object key, Object value) {
262: if (value != null) {
263: getClientProperties().put(key, value);
264: } else {
265: getClientProperties().remove(key);
266: }
267: getModel().nodeChanged(this );
268: }
269:
270: public abstract Class getObjectType();
271:
272: public abstract Class[] getChildTypes();
273:
274: public void stateChanged(ChangeEvent e) {
275: if (e.getSource() instanceof ValidationStatus) {
276: ValidationStatus validationStatus = (ValidationStatus) e
277: .getSource();
278: putClientProperty("issues", new HashSet(validationStatus
279: .getIssues()));
280: }
281: getModel().nodeChanged(this );
282: }
283:
284: public void connect() {
285: if (parent.getObject() instanceof TreeEntity) {
286: getModel().getTreeLogic().connectNodeToTree(
287: (TreeEntity) parent.getObject(),
288: (TreeNodeEntity) getObject());
289: } else if (parent.getObject() instanceof TreeNodeEntity) {
290: getModel().getTreeLogic().connectNodeToNode(
291: (TreeNodeEntity) parent.getObject(),
292: (TreeNodeEntity) getObject());
293: }
294: }
295:
296: public void buildContext(ObjectContext context) {
297: GenericObjectTreeNode parentTreeNode = getParent();
298: if (parentTreeNode != null)
299: parentTreeNode.buildContext(context);
300:
301: context.putObject("node", this );
302: installContext(context);
303: }
304:
305: protected void installContext(ObjectContext context) {
306: }
307:
308: public abstract O loadObject();
309:
310: public static final class BreadthFirstEnumeration implements
311: Enumeration<TreeNode> {
312: protected GenericObjectTreeNode.BreadthFirstEnumeration.Queue queue;
313:
314: public BreadthFirstEnumeration(TreeNode rootNode) {
315: super ();
316: Vector v = new Vector(1);
317: v.addElement(rootNode);
318: queue = new GenericObjectTreeNode.BreadthFirstEnumeration.Queue();
319: queue.enqueue(v.elements());
320: }
321:
322: public boolean hasMoreElements() {
323: return (!queue.isEmpty() && ((Enumeration) queue
324: .firstObject()).hasMoreElements());
325: }
326:
327: public TreeNode nextElement() {
328: Enumeration enumer = (Enumeration) queue.firstObject();
329: TreeNode node = (TreeNode) enumer.nextElement();
330: Enumeration children = node.children();
331:
332: if (!enumer.hasMoreElements()) {
333: queue.dequeue();
334: }
335: if (children.hasMoreElements()) {
336: queue.enqueue(children);
337: }
338: return node;
339: }
340:
341: // A simple queue with a linked list data structure.
342: final class Queue {
343: GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode head; // null if empty
344: GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode tail;
345:
346: final class QNode {
347: public Object object;
348: public GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode next; // null if end
349:
350: public QNode(
351: Object object,
352: GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode next) {
353: this .object = object;
354: this .next = next;
355: }
356: }
357:
358: public void enqueue(Object anObject) {
359: if (head == null) {
360: head = tail = new GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode(
361: anObject, null);
362: } else {
363: tail.next = new GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode(
364: anObject, null);
365: tail = tail.next;
366: }
367: }
368:
369: public Object dequeue() {
370: if (head == null) {
371: throw new NoSuchElementException("No more elements");
372: }
373:
374: Object retval = head.object;
375: GenericObjectTreeNode.BreadthFirstEnumeration.Queue.QNode oldHead = head;
376: head = head.next;
377: if (head == null) {
378: tail = null;
379: } else {
380: oldHead.next = null;
381: }
382: return retval;
383: }
384:
385: public Object firstObject() {
386: if (head == null) {
387: throw new NoSuchElementException("No more elements");
388: }
389:
390: return head.object;
391: }
392:
393: public boolean isEmpty() {
394: return head == null;
395: }
396: }
397: }
398: }
|