001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.tax.event;
042:
043: import java.lang.reflect.*;
044:
045: import java.util.Map;
046: import java.util.List;
047: import java.util.Iterator;
048: import java.util.LinkedList;
049:
050: import java.beans.PropertyChangeListener;
051: import java.beans.PropertyChangeSupport;
052:
053: import org.netbeans.tax.TreeObject;
054: import java.util.Set;
055:
056: /**
057: *
058: * @author Libor Kramolis
059: * @version 0.1
060: */
061: public final class TreeEventChangeSupport {
062:
063: /** Utility field used by bound properties. */
064: private PropertyChangeSupport propertyChangeSupport;
065:
066: /** Event source. */
067: private TreeObject eventSource;
068:
069: /** Its event cache. */
070: private EventCache eventCache;
071:
072: //
073: // init
074: //
075:
076: /** Creates new TreeEventChangeSupport. */
077: public TreeEventChangeSupport(TreeObject eventSource) {
078: this .eventSource = eventSource;
079: this .eventCache = new EventCache();
080: }
081:
082: //
083: // itself
084: //
085:
086: /**
087: */
088: public final TreeEvent createEvent(String propertyName,
089: Object oldValue, Object newValue) {
090: return new TreeEvent(eventSource, propertyName, oldValue,
091: newValue);
092: }
093:
094: /**
095: */
096: protected final TreeObject getEventSource() {
097: return eventSource;
098: }
099:
100: /**
101: */
102: private final PropertyChangeSupport getPropertyChangeSupport() {
103: if (propertyChangeSupport == null) {
104: propertyChangeSupport = new PropertyChangeSupport(
105: eventSource);
106: }
107: return propertyChangeSupport;
108: }
109:
110: /** Add a PropertyChangeListener to the listener list.
111: * @param listener The listener to add.
112: */
113: public final void addPropertyChangeListener(
114: PropertyChangeListener listener) {
115: getPropertyChangeSupport().addPropertyChangeListener(listener);
116:
117: if (Util.THIS.isLoggable()) /* then */{
118: Util.THIS
119: .debug("TreeEventChangeSupport::addPropertyChangeListener: listener = "
120: + listener); // NOI18N
121: Util.THIS.debug(" propertyChangeSupport = "
122: + listListeners()); // NOI18N
123: if (listener == null) {
124: Util.THIS.debug(" eventSource = " + eventSource); // NOI18N
125: }
126: }
127: }
128:
129: /**
130: */
131: public final void addPropertyChangeListener(String propertyName,
132: PropertyChangeListener listener) {
133: getPropertyChangeSupport().addPropertyChangeListener(
134: propertyName, listener);
135:
136: if (Util.THIS.isLoggable()) /* then */{
137: Util.THIS
138: .debug("TreeEventChangeSupport::addPropertyChangeListener: propertyName = "
139: + propertyName); // NOI18N
140: Util.THIS.debug(" listener = " + listener); // NOI18N
141: Util.THIS.debug(" propertyChangeSupport = "
142: + listListeners()); // NOI18N
143: if (listener == null) {
144: Util.THIS.debug(" eventSource = " + eventSource); // NOI18N
145: Util.THIS
146: .debug(new RuntimeException(
147: "TreeEventChangeSupport.addPropertyChangeListener")); // NOI18N
148: }
149: }
150: }
151:
152: /** Removes a PropertyChangeListener from the listener list.
153: * @param listener The listener to remove.
154: */
155: public final void removePropertyChangeListener(
156: PropertyChangeListener listener) {
157: getPropertyChangeSupport().removePropertyChangeListener(
158: listener);
159:
160: if (Util.THIS.isLoggable()) /* then */{
161: Util.THIS
162: .debug("TreeEventChangeSupport::removePropertyChangeListener: listener = "
163: + listener); // NOI18N
164: Util.THIS.debug(" propertyChangeSupport = "
165: + listListeners()); // NOI18N
166: }
167: }
168:
169: /**
170: */
171: public final void removePropertyChangeListener(String propertyName,
172: PropertyChangeListener listener) {
173: getPropertyChangeSupport().removePropertyChangeListener(
174: propertyName, listener);
175:
176: if (Util.THIS.isLoggable()) /* then */{
177: Util.THIS
178: .debug("TreeEventChangeSupport::removePropertyChangeListener: propertyName = "
179: + propertyName); // NOI18N
180: Util.THIS.debug(" listener = " + listener); // NOI18N
181: Util.THIS
182: .debug("- ::removePropertyChangeListener: propertyChangeSupport = "
183: + listListeners()); // NOI18N
184: }
185: }
186:
187: /**
188: * Check if there are any listeners for a specific property.
189: *
190: * @param propertyName the property name.
191: * @return true if there are ore or more listeners for the given property
192: */
193: public final boolean hasPropertyChangeListeners(String propertyName) {
194: return getPropertyChangeSupport().hasListeners(propertyName);
195: }
196:
197: /**
198: * Fire an existing PropertyChangeEvent to any registered listeners.
199: * No event is fired if the given event's old and new values are
200: * equal and non-null.
201: * @param evt The PropertyChangeEvent object.
202: */
203: public final void firePropertyChange(TreeEvent evt) {
204: if (Util.THIS.isLoggable()) /* then */{
205: Util.THIS
206: .debug("TreeEventChangeSupport::firePropertyChange ( "
207: + evt + " )"); // NOI18N
208: Util.THIS.debug(" eventSource = " + eventSource); // NOI18N
209: Util.THIS.debug(" EventManager = "
210: + eventSource.getEventManager()); // NOI18N
211: }
212:
213: if (eventSource.getEventManager() == null)
214: return;
215: eventSource.getEventManager().firePropertyChange(this , evt);
216: }
217:
218: /**
219: */
220: protected final void firePropertyChangeNow(TreeEvent evt) {
221: getPropertyChangeSupport().firePropertyChange(evt);
222: }
223:
224: /**
225: */
226: protected final void firePropertyChangeLater(TreeEvent evt) {
227: eventCache.addEvent(evt);
228: }
229:
230: /**
231: */
232: protected final void firePropertyChangeCache() {
233: eventCache.firePropertyChange();
234: }
235:
236: /**
237: */
238: protected final void clearPropertyChangeCache() {
239: eventCache.clear();
240: }
241:
242: //
243: // debug
244: //
245:
246: /**
247: */
248: private String listListeners(Object instance) {
249: try {
250: Class klass = instance.getClass();
251: Field field = klass.getDeclaredField("listeners"); // NOI18N
252: field.setAccessible(true);
253:
254: return field.get(instance).toString();
255: } catch (Exception ex) {
256: return "" + ex.getClass() + " " + ex.getMessage(); // NOI18N
257: }
258: }
259:
260: /**
261: */
262: private String listChildrenListeners(PropertyChangeSupport support) {
263: try {
264: Object instance = support;
265: Class klass = instance.getClass();
266: Field field = klass.getDeclaredField("children"); // NOI18N
267: field.setAccessible(true);
268:
269: StringBuffer sb = new StringBuffer();
270: Map map = (Map) field.get(instance);
271: if (map == null)
272: return "";
273: Set keys = map.keySet();
274: Iterator it = keys.iterator();
275: while (it.hasNext()) {
276: Object key = it.next();
277: sb.append("\n[").append(key).append("] ").append(
278: listListeners(map.get(key))); // NOI18N
279: }
280:
281: return sb.toString();
282: } catch (Exception ex) {
283: ex.printStackTrace();
284: return "<" + ex + ">"; // NOI18N
285: }
286: }
287:
288: /**
289: * For debug purposes list all registered listeners
290: */
291: public final String listListeners() {
292: StringBuffer sb = new StringBuffer();
293:
294: sb.append("[*general*] ").append(
295: listListeners(getPropertyChangeSupport())).append("\n"); // NOI18N
296: sb.append(listChildrenListeners(getPropertyChangeSupport()));
297:
298: return sb.toString();
299: }
300:
301: //
302: // Event Cache
303: //
304:
305: /**
306: * EventCache for later event firing.
307: */
308: private class EventCache {
309:
310: /** */
311: List eventList;
312:
313: //
314: // init
315: //
316:
317: /** Creates new EventCache. */
318: public EventCache() {
319: eventList = new LinkedList();
320: }
321:
322: //
323: // itself
324: //
325:
326: /**
327: */
328: public void clear() {
329: synchronized (eventList) {
330: eventList.clear();
331: }
332: }
333:
334: /**
335: */
336: public void addEvent(TreeEvent event) {
337: synchronized (eventList) {
338: eventList.add(event);
339: }
340: }
341:
342: /**
343: */
344: public void firePropertyChange() {
345: List listCopy;
346: synchronized (eventList) {
347: listCopy = new LinkedList(eventList);
348: eventList.clear();
349: }
350: Iterator it = listCopy.iterator();
351: while (it.hasNext()) {
352: firePropertyChangeNow((TreeEvent) it.next());
353: }
354: }
355:
356: } // end: class EventCache
357:
358: }
|