001: /*
002: * $RCSfile: WritablePropertySourceImpl.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:25 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.beans.PropertyChangeListener;
015: import java.util.Collections;
016: import java.util.Hashtable;
017: import java.util.HashSet;
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.Set;
021: import javax.media.jai.util.CaselessStringKey;
022:
023: /**
024: * A utility implementation of the <code>WritablePropertySource</code>
025: * interface. The same internal superclass data structures are used and
026: * are supplemented by a <code>PropertyChangeSupportJAI</code> to handle
027: * property event firing. All events fired by an instance of this class
028: * will be <code>PropertySourceChangeEvent</code>s with an event source
029: * equal to the object used to create the <code>PropertyChangeSupportJAI</code>
030: * helper object. This object is user-specifiable at construction time or
031: * automatically created when a <code>PropertyChangeListener</code> is
032: * added or removed and the <code>PropertyChangeSupportJAI</code> specified
033: * at construction was <code>null</code>.
034: *
035: * @see CaselessStringKey
036: * @see PropertySource
037: * @see PropertySourceChangeEvent
038: * @see PropertySourceImpl
039: * @see WritablePropertySource
040: * @see PropertyChangeEmitter
041: * @see PropertyChangeSupportJAI
042: *
043: * @since JAI 1.1
044: */
045: public class WritablePropertySourceImpl extends PropertySourceImpl
046: implements WritablePropertySource {
047:
048: /**
049: * Helper object for bean-style property events. Its default
050: * value is <code>null</code> which indicates that no events
051: * are to be fired.
052: */
053: protected PropertyChangeSupportJAI manager = null;
054:
055: /**
056: * Constructs a <code>WritablePropertySourceImpl</code> instance with
057: * no properties set.
058: */
059: public WritablePropertySourceImpl() {
060: super ();
061: }
062:
063: /**
064: * Constructs a <code>WritablePropertySourceImpl</code> instance which
065: * will derive properties from one or both of the supplied parameters.
066: * The <code>propertyMap</code> and <code>propertySource</code> parameters
067: * will be used to initialize the name-value and
068: * name-<code>PropertySource</code> mappings, respectively.
069: * Entries in the <code>propertyMap</code> object will be assumed
070: * to be properties if the key is a <code>String</code> or a
071: * <code>CaselessStringKey</code>. The <code>propertySource</code>
072: * object will be queried for the names of properties that it emits
073: * but requests for associated values will not be made at this time
074: * so as to to defer any calculation that such requests might provoke.
075: * The case of property names will be retained but will be ignored
076: * insofar as the name is used as a key to the property value.
077: *
078: * @param propertyMap A <code>Map</code> from which to copy properties
079: * which have keys which are either <code>String</code>s or
080: * <code>CaselessStringKey</code>s.
081: * @param propertySource A <code>PropertySource</code> from which to
082: * derive properties.
083: * @param manager The object which will actually fire the events.
084: * May be <code>null</code> in which case a default
085: * event manager will be created as needed with this
086: * object as its event source.
087: */
088: public WritablePropertySourceImpl(Map propertyMap,
089: PropertySource source, PropertyChangeSupportJAI manager) {
090: super (propertyMap, source);
091: this .manager = manager;
092: }
093:
094: /**
095: * Returns the value of a property. If the property name is not
096: * recognized, <code>java.awt.Image.UndefinedProperty</code> will
097: * be returned.
098: *
099: * <p> If the requested name is found in the name-value mapping,
100: * the corresponding value will be returned. Otherwise the
101: * name-<code>PropertySource</code> mapping will be queried and the
102: * value will be derived from the found <code>PropertySource</code>,
103: * if any. If the value is derived from a <code>PropertySource</code>,
104: * a record will be kept of this and this property will be referred to
105: * as a "cached property". Derivation of the value from a
106: * <code>PropertySource</code> rather than from the name-value
107: * mapping may cause a <code>PropertySourceChangeEvent<code> to be fired.
108: *
109: * @param propertyName the name of the property, as a <code>String</code>.
110: *
111: * @return the value of the property, as an
112: * <code>Object</code>, or the value
113: * <code>java.awt.Image.UndefinedProperty</code>.
114: *
115: * @exception IllegalArgumentException if <code>propertyName</code>
116: * is <code>null</code>.
117: */
118: public Object getProperty(String propertyName) {
119: if (propertyName == null) {
120: throw new IllegalArgumentException(JaiI18N
121: .getString("Generic0"));
122: }
123:
124: synchronized (properties) {
125: // Check whether a value mapping exists.
126: boolean isMapped = properties
127: .containsKey(new CaselessStringKey(propertyName));
128:
129: // Retrieve the value.
130: Object value = super .getProperty(propertyName);
131:
132: // Fire an event if necessary, i.e., if there is an event manager
133: // and the property value was just derived from a PropertySource in
134: // the name-PropertySource mapping.
135: if (manager != null && !isMapped
136: && value != java.awt.Image.UndefinedProperty) {
137: // Value was derived from a PropertySource -> event.
138: Object eventSource = manager
139: .getPropertyChangeEventSource();
140: PropertySourceChangeEvent evt = new PropertySourceChangeEvent(
141: eventSource, propertyName,
142: java.awt.Image.UndefinedProperty, value);
143: manager.firePropertyChange(evt);
144: }
145:
146: return value;
147: }
148: }
149:
150: /**
151: * Adds the property value associated with the supplied name to
152: * the <code>PropertySource</code>. Values set by this means will
153: * supersede any previously defined value of the property.
154: *
155: * <p> <code>PropertySourceChangeEvent<code>s may be fired
156: * with a name set to that of the property (retaining case) and old and
157: * new values set to the previous and current values of the property,
158: * respectively. The value returned by the <code>getSource()</code>
159: * method of the event is determined by the value passed to the
160: * constructor of the <code>PropertyChangeSupportJAI</code> parameter.
161: * Neither the old nor the new value may be <code>null</code>: undefined
162: * properties must as usual be indicated by the constant value
163: * <code>java.awt.Image.UndefinedProperty</code>. It is however
164: * legal for either but not both of the old and new property values
165: * to equal <code>java.awt.Image.UndefinedProperty</code>.
166: *
167: * @param propertyName the name of the property, as a <code>String</code>.
168: * @param propertyValue the property, as a general <code>Object</code>.
169: *
170: * @exception IllegalArgumentException if <code>propertyName</code>
171: * or <code>propertyValue</code>
172: * is <code>null</code>.
173: */
174: public void setProperty(String propertyName, Object propertyValue) {
175: if (propertyName == null || propertyValue == null) {
176: throw new IllegalArgumentException(JaiI18N
177: .getString("Generic0"));
178: }
179:
180: synchronized (properties) {
181: CaselessStringKey key = new CaselessStringKey(propertyName);
182:
183: // Set the entry in the name-value mapping.
184: Object oldValue = properties.put(key, propertyValue);
185: if (oldValue == null) {
186: oldValue = java.awt.Image.UndefinedProperty;
187: }
188:
189: // Suppress the name if present in the cached properties listing.
190: cachedPropertyNames.remove(key);
191:
192: if (manager != null && !oldValue.equals(propertyValue)) {
193: Object eventSource = manager
194: .getPropertyChangeEventSource();
195: PropertySourceChangeEvent evt = new PropertySourceChangeEvent(
196: eventSource, propertyName, oldValue,
197: propertyValue);
198: manager.firePropertyChange(evt);
199: }
200: }
201: }
202:
203: /**
204: * Removes the named property from the <code>PropertySource</code>.
205: * <code>PropertySourceChangeEvent<code>s may be fired if the property
206: * values is actually removed from the name-value mapping. In this case
207: * the new value of the property event will be
208: * <code>java.awt.Image.UndefinedProperty</code>.
209: *
210: * @param propertyName the name of the property, as a <code>String</code>.
211: * @param propertyValue the property, as a general <code>Object</code>.
212: *
213: * @exception IllegalArgumentException if <code>propertyName</code>
214: * is <code>null</code>.
215: */
216: public void removeProperty(String propertyName) {
217: if (propertyName == null) {
218: throw new IllegalArgumentException(JaiI18N
219: .getString("Generic0"));
220: }
221:
222: synchronized (properties) {
223: CaselessStringKey key = new CaselessStringKey(propertyName);
224:
225: // Remove the entry from the name-value mapping and save its value.
226: Object oldValue = properties.remove(key);
227:
228: // Remove the entry from the name-PropertySource mapping and from
229: // the list of cached properties.
230: propertySources.remove(key);
231: cachedPropertyNames.remove(key);
232:
233: if (manager != null && oldValue != null) {
234: Object eventSource = manager
235: .getPropertyChangeEventSource();
236: PropertySourceChangeEvent evt = new PropertySourceChangeEvent(
237: eventSource, propertyName, oldValue,
238: java.awt.Image.UndefinedProperty);
239: manager.firePropertyChange(evt);
240: }
241: }
242: }
243:
244: /**
245: * Copies from the supplied <code>Map</code> to the property set all
246: * entries wherein the key is an instance of <code>String</code>
247: * or <code>CaselessStringKey</code>. Values set by this means will
248: * supersede any previously defined values of the respective properties.
249: * All property values are copied by reference.
250: * <code>PropertySourceChangeEvent<code>s may be fired for each
251: * property added. If the property was not previously defined the
252: * old value of the property event will be
253: * <code>java.awt.Image.UndefinedProperty</code>.
254: *
255: * @param propertyMap A <code>Map</code> from which to copy properties
256: * which have keys which are either <code>String</code>s or
257: * <code>CaselessStringKey</code>s. If <code>null</code> no
258: * properties will be added.
259: */
260: public void addProperties(Map propertyMap) {
261: if (propertyMap != null) {
262: synchronized (properties) {
263: Iterator keys = propertyMap.keySet().iterator();
264: while (keys.hasNext()) {
265: Object key = keys.next();
266: if (key instanceof String) {
267: setProperty((String) key, propertyMap.get(key));
268: } else if (key instanceof CaselessStringKey) {
269: setProperty(
270: ((CaselessStringKey) key).getName(),
271: propertyMap.get(key));
272: }
273: }
274: }
275: }
276: }
277:
278: /**
279: * Adds a <code>PropertySource</code> to the
280: * name-<code>PropertySource</code> mapping. The
281: * actual property values are not requested at this time but instead
282: * an entry for the name of each property emitted by the
283: * <code>PropertySource</code> is added to the
284: * name-<code>PropertySource</code> mapping. Properties defined by
285: * this <code>PropertySource</code> supersede those of all other
286: * previously added <code>PropertySource</code>s including that
287: * specified at construction, if any. Note that this method will not
288: * provoke any events as no properties will actually have changed.
289: *
290: * @param propertySource A <code>PropertySource</code> from which to
291: * derive properties. If <code>null</code> nothing is done.
292: */
293: public void addProperties(PropertySource propertySource) {
294: if (propertySource != null) {
295: synchronized (properties) {
296: String[] names = propertySource.getPropertyNames();
297: if (names != null) {
298: int length = names.length;
299: for (int i = 0; i < length; i++) {
300: propertySources.put(new CaselessStringKey(
301: names[i]), propertySource);
302: }
303: }
304: }
305: }
306: }
307:
308: /**
309: * Clears all properties from the <code>PropertySource</code> by
310: * invoking <code>removeProperty()</code> with all known names.
311: * <code>PropertySourceChangeEvent<code>s may be fired
312: * for each stored property removed. In this case the new value
313: * of the property event will be
314: * <code>java.awt.Image.UndefinedProperty</code>.
315: */
316: public void clearProperties() {
317: synchronized (properties) {
318: String[] names = getPropertyNames();
319: if (names != null) {
320: int length = names.length;
321: for (int i = 0; i < length; i++) {
322: removeProperty(names[i]);
323: }
324: }
325: }
326: }
327:
328: /**
329: * Clears the name-value mapping of all properties.
330: * <code>PropertySourceChangeEvent<code>s may be fired
331: * for each stored property removed. In this case the new value
332: * of the property event will be
333: * <code>java.awt.Image.UndefinedProperty</code>.
334: */
335: public void clearPropertyMap() {
336: synchronized (properties) {
337: Iterator keys = properties.keySet().iterator();
338: while (keys.hasNext()) {
339: CaselessStringKey key = (CaselessStringKey) keys.next();
340: Object oldValue = properties.get(key);
341: keys.remove();
342:
343: if (manager != null) {
344: Object eventSource = manager
345: .getPropertyChangeEventSource();
346: PropertySourceChangeEvent evt = new PropertySourceChangeEvent(
347: eventSource, key.getName(), oldValue,
348: java.awt.Image.UndefinedProperty);
349: manager.firePropertyChange(evt);
350: }
351: }
352:
353: // Also clear cached names.
354: cachedPropertyNames.clear();
355: }
356: }
357:
358: /**
359: * Clears the name-<code>PropertySource</code> mapping.
360: * No events will be fired.
361: */
362: public void clearPropertySourceMap() {
363: synchronized (properties) {
364: propertySources.clear();
365: }
366: }
367:
368: /**
369: * Clears from the name-value mapping all properties which were
370: * derived from a source in the name-<code>PropertySource</code> mapping.
371: * <code>PropertySourceChangeEvent<code>s may be fired
372: * for each stored property removed. In this case the new value
373: * of the property event will be
374: * <code>java.awt.Image.UndefinedProperty</code>.
375: */
376: public void clearCachedProperties() {
377: synchronized (properties) {
378: Iterator names = cachedPropertyNames.iterator();
379: while (names.hasNext()) {
380: CaselessStringKey name = (CaselessStringKey) names
381: .next();
382: Object oldValue = properties.remove(name);
383: names.remove(); // remove name from cachedPropertyNames.
384: if (manager != null) {
385: Object eventSource = manager
386: .getPropertyChangeEventSource();
387: PropertySourceChangeEvent evt = new PropertySourceChangeEvent(
388: eventSource, name.getName(), oldValue,
389: java.awt.Image.UndefinedProperty);
390: manager.firePropertyChange(evt);
391: }
392: }
393: }
394: }
395:
396: /**
397: * Removes from the name-<code>PropertySource</code> mapping all entries
398: * which refer to the supplied <code>PropertySource</code>.
399: */
400: public void removePropertySource(PropertySource propertySource) {
401: synchronized (properties) {
402: Iterator keys = propertySources.keySet().iterator();
403: while (keys.hasNext()) {
404: Object ps = propertySources.get(keys.next());
405: if (ps.equals(propertySource)) {
406: keys.remove(); // remove this entry from propertySources.
407: }
408: }
409: }
410: }
411:
412: /**
413: * Add a <code>PropertyChangeListener</code> to the listener list. The
414: * listener is registered for all properties.
415: *
416: * <p> If the property event utility object was not set at construction,
417: * then it will be initialized to a <code>PropertyChangeSupportJAI</code>
418: * whose property event source is this object.
419: */
420: public void addPropertyChangeListener(
421: PropertyChangeListener listener) {
422: getEventManager().addPropertyChangeListener(listener);
423: }
424:
425: /**
426: * Add a <code>PropertyChangeListener</code> for a specific property. The
427: * listener will be invoked only when a call on
428: * firePropertyChange names that specific property.
429: *
430: * <p> If the property event utility object was not set at construction,
431: * then it will be initialized to a <code>PropertyChangeSupportJAI</code>
432: * whose property event source is this object.
433: */
434: public void addPropertyChangeListener(String propertyName,
435: PropertyChangeListener listener) {
436: getEventManager().addPropertyChangeListener(propertyName,
437: listener);
438: }
439:
440: /**
441: * Remove a <code>PropertyChangeListener</code> from the listener list.
442: * This removes a <code>PropertyChangeListener</code> that was registered
443: * for all properties.
444: *
445: * <p> If the property event utility object was not set at construction,
446: * then it will be initialized to a <code>PropertyChangeSupportJAI</code>
447: * whose property event source is this object.
448: */
449: public void removePropertyChangeListener(
450: PropertyChangeListener listener) {
451: getEventManager().removePropertyChangeListener(listener);
452: }
453:
454: /**
455: * Remove a <code>PropertyChangeListener</code> for a specific property.
456: *
457: * <p> If the property event utility object was not set at construction,
458: * then it will be initialized to a <code>PropertyChangeSupportJAI</code>
459: * whose property event source is this object.
460: */
461: public void removePropertyChangeListener(String propertyName,
462: PropertyChangeListener listener) {
463: getEventManager().removePropertyChangeListener(propertyName,
464: listener);
465: }
466:
467: /**
468: * Returns the utility property event manager. If none has been set,
469: * initialize it to one whose source is this object.
470: */
471: private PropertyChangeSupportJAI getEventManager() {
472: if (manager == null) {
473: synchronized (this ) {
474: manager = new PropertyChangeSupportJAI(this);
475: }
476: }
477: return manager;
478: }
479: }
|