KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > tax > TreeObject


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.tax;
20
21 import java.io.PrintStream JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23
24 import org.netbeans.tax.event.TreeEventManager;
25 import org.netbeans.tax.event.TreeEventModel;
26 import org.netbeans.tax.event.TreeEvent;
27 import org.netbeans.tax.event.TreeEventChangeSupport;
28
29 /**
30  * Tree objects base class with support for firing <b>events</b> and <b>merging</b>.
31  * <p>
32  * It also prescribes that each subclass MUST have <b>copy constuctor</b>
33  * calling its superclass copy constructor. The copy constructor MUST be then called
34  * during <b>cloning</b>.
35  * <p>
36  * All TreeObject subclasses should not have public contructors and therefore
37  * should be created just by factory methods.
38  * <p>
39  * Pending: validation on request, invalidation
40  *
41  * @author Libor Kramolis
42  * @author Petr Kuzel
43  * @version 0.1
44  */

45 public abstract class TreeObject implements TreeEventModel {
46     
47     /** */
48     public static final String JavaDoc PROP_READ_ONLY = "readOnly"; // NOI18N
49

50     /** */
51     private boolean readOnly;
52     
53     /** */
54     transient private TreeEventChangeSupport eventChangeSupport;
55     
56     
57     //
58
// init
59
//
60

61     
62     /** Creates new TreeObject. */
63     protected TreeObject () {
64         this.readOnly = false;
65         this.eventChangeSupport = null;
66     }
67     
68     /**
69      * Creates new TreeObject - copy constructor.
70      * (it does not copy eventChangeSupport)
71      */

72     protected TreeObject (TreeObject object) {
73         this.readOnly = object.readOnly;
74         this.eventChangeSupport = null;
75     }
76     
77     
78     //
79
// clone
80
//
81

82     /**
83      * Cloning must use copy constructors!
84      */

85     public abstract Object JavaDoc clone ();
86     
87     
88     //
89
// util
90
//
91

92     /**
93      */

94     protected final boolean isInstance (Object JavaDoc object) {
95         return ( this.getClass ().isInstance (object) );
96     }
97     
98     
99     //
100
// context
101
//
102

103     /**
104      */

105     abstract public boolean isInContext ();
106     
107     /**
108      */

109     abstract public void removeFromContext () throws ReadOnlyException;
110     
111     
112     //
113
// equals
114
//
115

116     /**
117      */

118     public /*final*/ boolean equals (Object JavaDoc object) {
119         return super.equals (object);
120         
121         // when TreeObjectList will compare inserted object by 'instance' instead of 'equals' we should final this method and use this impl:
122
// return equals (object, true);
123
}
124     
125     /**
126      */

127     public boolean equals (Object JavaDoc object, boolean deep) {
128         if (!!! isInstance (object))
129             return false;
130         
131         TreeObject peer = (TreeObject) object;
132         
133         return (this.readOnly == peer.readOnly);
134     }
135     
136     
137     //
138
// merge
139
//
140

141     /**
142      * <p>Update algorithm pattern that <b>reuses original tree instances</b>:
143      * <pre>
144      * // 1. optimalization
145      * if (this == treeObject) return;
146      *
147      * // 2. can merge just my instances (so no cross implemetation merge allowed)
148      * if (getClass().isAssignablFrom(treeObject.getClass())) throw CannotMergeException;
149      *
150      * // 3. let superclass do its merge
151      * super.merge(treeObject);
152      *
153      * // 4. cast to myself (see step 2)
154      * {getClass()} peer = ({getClass()}) treeObject;
155      *
156      * // 5. merge all fields at THIS CLASS HIEARCHY LEVEL but
157      * // fields that references object "parents"
158      * // use setters that just fires property changes, i.e. such that never fails
159      * // due to read-only or other constrains checks
160      *
161      * foreach field in suitableClassFields
162      * if field is simple
163      * set{field}Impl( peer.get{field}() )
164      * if field is collection or TreeObject
165      * {field}.merge(peer.{field})
166      * next field
167      *
168      * </pre>
169      * @param treeobject merge peer
170      * @throws CannotMergeException if can not merge with given node (invalid class)
171      */

172     public void merge (TreeObject treeObject) throws CannotMergeException {
173         if (treeObject == this)
174             return;
175         
176         checkMergeObject (treeObject);
177         
178         TreeObject peer = treeObject;
179         
180         setReadOnly (peer.isReadOnly ());
181     }
182     
183     /**
184      */

185     protected final void checkMergeObject (TreeObject treeObject) throws CannotMergeException {
186         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TreeObject::checkMergeObject: this = " + this); // NOI18N
187
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" ::checkMergeObject: treeObject = " + treeObject); // NOI18N
188
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" checkMergeObject: isSameClass ? " + isInstance (treeObject)); // NOI18N
189

190         if ( (treeObject == null) || (!!! isInstance (treeObject)) ) {
191             throw new CannotMergeException (treeObject);
192         }
193     }
194     
195     //
196
// read only
197
//
198

199     /**
200      */

201     public final boolean isReadOnly () {
202         return readOnly;
203     }
204     
205     /**
206      */

207     protected void setReadOnly (boolean newReadOnly) {
208         if (readOnly == newReadOnly)
209             return;
210         
211         boolean oldReadOnly = this.readOnly;
212         this.readOnly = newReadOnly;
213         firePropertyChange (getEventChangeSupport ().createEvent (PROP_READ_ONLY, oldReadOnly ? Boolean.TRUE : Boolean.FALSE, newReadOnly ? Boolean.TRUE : Boolean.FALSE));
214     }
215     
216     /**
217      */

218     protected final void checkReadOnly () throws ReadOnlyException {
219         if (readOnly == true) {
220             throw new ReadOnlyException (this);
221         }
222     }
223     
224     
225     
226     //
227
// event model
228
//
229

230     /**
231      * @return support that delegates to TreeEventManager
232      */

233     protected final TreeEventChangeSupport getEventChangeSupport () {
234         if (eventChangeSupport == null) {
235             eventChangeSupport = new TreeEventChangeSupport (this);
236         }
237         return eventChangeSupport;
238     }
239     
240     /**
241      * Get assigned event manager.
242      * Whole document should have only one and same EventManager. When there is not
243      * available manager, it returns null.
244      *
245      * @return assigned event manager (may be null).
246      */

247     public abstract TreeEventManager getEventManager ();
248     
249     
250     /**
251      */

252     // protected final void addEventManagerChangeListener (PropertyChangeListener listener) {
253
// getEventChangeSupport().addPropertyChangeListener (PROP_EVENT_MANAGER, listener);
254
// }
255

256     /**
257      */

258     // protected final void removeEventManagerChangeListener (PropertyChangeListener listener) {
259
// getEventChangeSupport().removePropertyChangeListener (PROP_EVENT_MANAGER, listener);
260
// }
261

262     /**
263      */

264     public final void addReadonlyChangeListener (PropertyChangeListener JavaDoc listener) {
265         getEventChangeSupport ().addPropertyChangeListener (PROP_READ_ONLY, listener);
266     }
267     
268     /**
269      */

270     public final void removeReadonlyChangeListener (PropertyChangeListener JavaDoc listener) {
271         getEventChangeSupport ().removePropertyChangeListener (PROP_READ_ONLY, listener);
272     }
273     
274     
275     /**
276      * Add a PropertyChangeListener to the listener list.
277      * @param listener The listener to add.
278      */

279     public final void addPropertyChangeListener (PropertyChangeListener JavaDoc listener) {
280         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("Tree " + this + "attached listener" + listener); // NOI18N
281

282         getEventChangeSupport ().addPropertyChangeListener (listener);
283     }
284     
285     
286     /**
287      * Removes a PropertyChangeListener from the listener list.
288      * @param listener The listener to remove.
289      */

290     public final void removePropertyChangeListener (PropertyChangeListener JavaDoc listener) {
291         getEventChangeSupport ().removePropertyChangeListener (listener);
292     }
293     
294     /**
295      * Fire an existing TreeEvent to any registered listeners.
296      * No event is fired if the given event's old and new values are
297      * equal and non-null.
298      * @param evt The TreeEvent object.
299      */

300     protected final void firePropertyChange (TreeEvent evt) {
301         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("TreeObject firing " + evt); // NOI18N
302

303         getEventChangeSupport ().firePropertyChange (evt);
304         bubblePropertyChange (evt);
305     }
306     
307     
308     /** Add a PropertyChangeListener for a specific property to the listener list.
309      * @param propertyname Name of the property to listen on.
310      * @param listener The listener to add.
311      */

312     public final void addPropertyChangeListener (String JavaDoc propertyName, PropertyChangeListener JavaDoc listener) {
313         getEventChangeSupport ().addPropertyChangeListener (propertyName, listener);
314     }
315     
316     /** Removes a PropertyChangeListener for a specific property from the listener list.
317      * @param propertyname Name of the property that was listened on.
318      * @param listener The listener to remove.
319      */

320     public final void removePropertyChangeListener (String JavaDoc propertyName, PropertyChangeListener JavaDoc listener) {
321         getEventChangeSupport ().removePropertyChangeListener (propertyName, listener);
322     }
323     
324     
325     /**
326      * Check if there are any listeners for a specific property.
327      *
328      * @param propertyName the property name.
329      * @return true if there are ore or more listeners for the given property
330      */

331     public final boolean hasPropertyChangeListeners (String JavaDoc propertyName) {
332         return getEventChangeSupport ().hasPropertyChangeListeners (propertyName);
333     }
334     
335     
336     /**
337      * Report a bound property update to any registered listeners.
338      * No event is fired if old and new are equal and non-null.
339      *
340      * @param propertyName The programmatic name of the property that was changed.
341      * @param oldValue The old value of the property.
342      * @param newValue The new value of the property.
343      */

344     protected final void firePropertyChange (String JavaDoc propertyName, Object JavaDoc oldValue, Object JavaDoc newValue) {
345         firePropertyChange (getEventChangeSupport ().createEvent (propertyName, oldValue, newValue));
346     }
347     
348     
349     /**
350      * Propagate event to parents' listeners.
351      */

352     protected final void bubblePropertyChange (TreeEvent origEvt) {
353         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug ("\nTreeObject [ " + this + " ]::bubblePropertyChange: origEvt = " + origEvt.getPropertyName ()); // NOI18N
354

355         TreeObject source = (TreeObject)origEvt.getSource ();
356         if ( source instanceof TreeAttribute ) {
357             TreeAttribute attr = (TreeAttribute)source;
358             TreeElement ownElem = attr.getOwnerElement ();
359             if ( ownElem != null ) {
360                 ownElem.firePropertyChange (TreeElement.PROP_ATTRIBUTES, attr, null);
361             }
362         } else if ( source instanceof TreeChild ) {
363             while ( source != null ) {
364                 TreeChild child = (TreeChild)source;
365                 TreeParentNode parent = child.getParentNode ();
366                 
367                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug (" ::bubblePropertyChange::parentNode = " + parent); // NOI18N
368

369                 if ( parent != null ) {
370                     parent.getEventChangeSupport ().firePropertyChange (origEvt.createBubbling (parent));
371                 }
372                 source = parent;
373             }
374         }
375     }
376     
377     
378     //
379
// debug
380
//
381

382     /**
383      * For debugging purposes.
384      */

385     public final String JavaDoc listListeners () {
386         return getEventChangeSupport ().listListeners ();
387     }
388     
389 }
390
Popular Tags