1 /*
2  * $RCSfile: CollectionImage.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:57:05 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.mediax.jai;
13 import java.awt.Image;
14 import java.beans.PropertyChangeListener;
15 import java.lang.ref.WeakReference;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Set;
21 
22 import com.lightcrafts.media.jai.util.PropertyUtil;
23 
24 /**
25  * An abstract superclass for classes representing a <code>Collection</code>
26  * of images.  It may be a <code>Collection</code> of
27  * <code>RenderedImage</code>s or <code>RenderableImage</code>s, a
28  * <code>Collection</code> of <code>Collection</code>s that include images.
29  * In other words, this class supports nested <code>Collection</code>s, but
30  * at the very bottom, there must be images associated with the
31  * <code>Collection</code> objects.
32  *
33  *
34  */
35 public abstract class CollectionImage implements ImageJAI, Collection {
36 
37     /**
38      * A <code>Collection</code> of objects.  It may be a
39      * <code>Collection</code> of images of the same type, a
40      * <code>Collection</code> of objects of the same type, each
41      * containing an image, or a <code>Collection</code> of
42      * <code>Collection</code>s whose leaf objects are images
43      * or objects that contain images.
44      */
45     protected Collection imageCollection;
46 
47     /**
48      * The <code>CollectionImageFactory</code> which created this
49      * <code>CollectionImage</code>; may be <code>null</code> which
50      * implies that the <code>CollectionImage</code> was not created
51      * by a <code>CollectionImageFactory</code>.
52      *
53      * @since JAI 1.1
54      */
55     protected CollectionImageFactory imageFactory;
56     private Boolean isFactorySet = Boolean.FALSE;
57 
58     /**
59      * A helper object to manage firing events.
60      *
61      * @since JAI 1.1
62      */
63     protected PropertyChangeSupportJAI eventManager = null;
64 
65     /**
66      * A helper object to manage the image properties.
67      *
68      * @since JAI 1.1
69      */
70     protected WritablePropertySourceImpl properties = null;
71 
72     /**
73      * A <code>Set</code> of <code>WeakReference</code>s to the
74      * sinks of this <code>CollectionImage</code>.
75      *
76      * @since JAI 1.1
77      */
78     protected Set sinks;
79 
80     /**
81      * Default constructor.  The <code>imageCollection</code> parameter is
82      * <code>null</code>.  Subclasses that use this constructor must either
83      * set the <code>imageCollection</code> parameter themselves, or override
84      * the methods defined in the <code>Collection</code> interface.
85      * Otherwise, a <code>NullPointerException</code> may be thrown at a later
86      * time when methods which use to the <code>imageCollection</code>
87      * instance variable are invoked.
88      */
CollectionImage()89     protected CollectionImage() {
90         eventManager = new PropertyChangeSupportJAI(this);
91         properties = new WritablePropertySourceImpl(null, null, eventManager);
92     }
93 
94     /**
95      * Constructs a class that contains an image <code>Collection</code>.
96      *
97      * @param collection  A <code>Collection</code> of objects that
98      * include images.
99      *
100      * @throws IllegalArgumentException if <code>collection</code> is
101      *         <code>null</code>.
102      */
CollectionImage(Collection collection)103     public CollectionImage(Collection collection) {
104         this();
105 
106 
107         if ( collection == null ) {
108             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
109         }
110 
111         imageCollection = collection;
112     }
113 
114     /* ----- Element retrieval method. ----- */
115 
116     /**
117      * Returns the element at the given index in <code>imageCollection</code>.
118      * If <code>imageCollection</code> is a <code>List</code> then the call is
119      * forwarded to <code>imageCollection</code>; otherwise an array is created
120      * by applying <code>toArray()</code> to <code>imageCollection</code> and
121      * the indicated element of that array is returned.  Note that in the
122      * latter case no guarantee as to element ordering beyond that stated in
123      * the specification of <code>Collection.toArray()</code>.
124      *
125      * @param index The index of the desired element.
126      * @throws IndexOutOfBoundsException if the index is out of range
127      *         (<code>index</code> &lt; 0 || <code>index</code> &ge;
128      *         <code>imageCollection.size()</code>).
129      *
130      * @since JAI 1.1
131      */
get(int index)132     public Object get(int index) {
133         if(index < 0 || index >= imageCollection.size()) {
134             throw new IndexOutOfBoundsException(); // No message needed.
135         }
136 
137         if(imageCollection instanceof List) {
138             return ((List)imageCollection).get(index);
139         } else {
140             return imageCollection.toArray((Object[])null)[index];
141         }
142     }
143 
144     /* ----- Image factory methods. ----- */
145 
146     /**
147      * Sets the <code>imageFactory</code> instance variable to the supplied
148      * value.  The parameter may be <code>null</code>.  It is recommended
149      * that this method be invoked as soon as the <code>CollectionImage</code>
150      * is constructed.
151      *
152      * @param imageFactory The creating <code>CollectionImageFactory</code> or
153      *        <code>null</code>
154      * @throws IllegalStateException if the corresponding instance variable
155      *                               was already set.
156      *
157      * @since JAI 1.1
158      */
setImageFactory(CollectionImageFactory imageFactory)159     public void setImageFactory(CollectionImageFactory imageFactory) {
160         synchronized(isFactorySet) {
161             if(isFactorySet.booleanValue()) {
162                 throw new IllegalStateException();
163             }
164             this.imageFactory = imageFactory;
165             isFactorySet = Boolean.TRUE;
166         }
167     }
168 
169     /**
170      * If this <code>CollectionImage</code> was created by a
171      * <code>CollectionImageFactory</code> then return a reference to
172      * that factory; otherwise return <code>null</code>.
173      *
174      * @since JAI 1.1
175      */
getImageFactory()176     public CollectionImageFactory getImageFactory() {
177         synchronized(isFactorySet) {
178             return imageFactory;
179         }
180     }
181 
182     /* ----- Sink methods. ----- */
183 
184     /**
185      * Adds a sink to the set of sinks.
186      *
187      * @since JAI 1.1
188      */
addSink(Object sink)189     public synchronized boolean addSink(Object sink) {
190         if(sink == null) {
191             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
192         }
193 
194         if(sinks == null) {
195             sinks = new HashSet();
196         }
197 
198         return sinks.add(new WeakReference(sink));
199     }
200 
201     /**
202      * Removes a sink from the set of sinks.
203      *
204      * @return <code>true</code> if and only if the set of sinks
205      * changed as a result of the call.
206      *
207      * @since JAI 1.1
208      */
removeSink(Object sink)209     public synchronized boolean removeSink(Object sink) {
210         if (sink == null) {
211             throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
212         }
213 
214         if (sinks == null) {
215             return false;
216         }
217 
218         boolean result = false;
219         Iterator it = sinks.iterator();
220         while(it.hasNext()) {
221             Object referent = ((WeakReference)it.next()).get();
222             if(referent == sink) {
223                 // Remove the sink.
224                 it.remove();
225                 result = true;
226                 // Do not break: could be more than one.
227             } else if(referent == null) {
228                 // A cleared reference: might as well remove it.
229                 it.remove(); // ignore return value here.
230             }
231         }
232 
233         return result;
234 
235     }
236 
237     /**
238      * Retrieves the set of sinks or <code>null</code> if
239      * there are none.
240      *
241      * @since JAI 1.1
242      */
getSinks()243     public synchronized Set getSinks() {
244         Set v = null;
245 
246         if (sinks != null && sinks.size() > 0) {
247             v = new HashSet(sinks.size());
248 
249             Iterator it = sinks.iterator();
250             while(it.hasNext()) {
251                 Object o = ((WeakReference)it.next()).get();
252 
253                 if (o != null) {
254                     v.add(o);
255                 }
256             }
257 
258             if (v.size() == 0) {
259                 v = null;
260             }
261         }
262 
263         return v;
264     }
265 
266     /**
267      * Removes all sinks from the set of sinks.
268      *
269      * @since JAI 1.1
270      */
removeSinks()271     public synchronized void removeSinks() {
272         sinks = null;
273     }
274 
275     /* ----- WritablePropertySource methods. ----- */
276 
277     /**
278      * Returns an array of <code>String</code>s recognized as names by this
279      * property source.  If no property names match, <code>null</code>
280      * will be returned.
281      *
282      * @return An array of <code>String</code>s which are the valid
283      * property names or <code>null</code> if there are none.
284      */
getPropertyNames()285     public String[] getPropertyNames() {
286         return properties.getPropertyNames();
287     }
288 
289     /**
290      * Returns an array of <code>String</code>s recognized as names by
291      * this property source that begin with the supplied prefix.  If
292      * no property names are recognized, or no property names match,
293      * <code>null</code> will be returned.
294      * The comparison is done in a case-independent manner.
295      *
296      * <p> The default implementation calls
297      * <code>getPropertyNames()</code> and searches the list of names
298      * for matches.
299      *
300      * @return An array of <code>String</code>s giving the valid
301      *         property names or <code>null</code> if there are none.
302      *
303      * @throws <code>IllegalArgumentException</code> if <code>prefix</code>
304      * is <code>null</code>.
305      */
getPropertyNames(String prefix)306     public String[] getPropertyNames(String prefix) {
307         return PropertyUtil.getPropertyNames(getPropertyNames(), prefix);
308     }
309 
310     /**
311      * Returns the class expected to be returned by a request for
312      * the property with the specified name.  If this information
313      * is unavailable, <code>null</code> will be returned.
314      *
315      * @return The <code>Class</code> expected to be return by a
316      *         request for the value of this property or <code>null</code>.
317      *
318      * @exception IllegalArgumentException if <code>name</code>
319      *                                     is <code>null</code>.
320      *
321      * @since JAI 1.1
322      */
getPropertyClass(String name)323     public Class getPropertyClass(String name) {
324         return properties.getPropertyClass(name);
325     }
326 
327     /**
328      * Returns the specified property.  The default implementation
329      * returns <code>java.awt.Image.UndefinedProperty</code>.
330      *
331      * @exception IllegalArgumentException if <code>name</code>
332      *                                     is <code>null</code>.
333      */
getProperty(String name)334     public Object getProperty(String name) {
335         return properties.getProperty(name);
336     }
337 
338     /**
339      * Returns the specified property.  The default implementation
340      * returns <code>java.awt.Image.UndefinedProperty</code>.
341      *
342      * @exception IllegalArgumentException if <code>name</code>
343      *                                     is <code>null</code>.
344      * @deprecated as of JAI 1.1.
345      */
getProperty(String name, Collection collection)346     public Object getProperty(String name, Collection collection) {
347         return Image.UndefinedProperty;
348     }
349 
350     /**
351      * Sets a property on a <code>CollectionImage</code>.  Some
352      * <code>CollectionImage</code> subclasses may ignore attempts to set
353      * properties.
354      *
355      * @param name a <code>String</code> containing the property's name.
356      * @param value the property, as a general <code>Object</code>.
357      *
358      * @throws IllegalArgumentException  If <code>name</code> or
359      *         <code>value</code> is <code>null</code>.
360      *
361      * @since JAI 1.1
362      */
setProperty(String name, Object value)363     public void setProperty(String name, Object value) {
364         properties.setProperty(name, value);
365     }
366 
367     /**
368      * Removes the named property from the <code>CollectionImage</code>.
369      * Some <code>CollectionImage</code> subclasses may ignore attempts to
370      * remove properties.
371      *
372      * @since JAI 1.1
373      */
removeProperty(String name)374     public void removeProperty(String name) {
375         properties.removeProperty(name);
376     }
377 
378     /* ----- PropertyChangeEmitter methods. ----- */
379 
380     /**
381      * Add a PropertyChangeListener to the listener list. The
382      * listener is registered for all properties.
383      *
384      * @since JAI 1.1
385      */
addPropertyChangeListener(PropertyChangeListener listener)386     public void addPropertyChangeListener(PropertyChangeListener listener) {
387         eventManager.addPropertyChangeListener(listener);
388     }
389 
390     /**
391      * Add a PropertyChangeListener for a specific property. The
392      * listener will be invoked only when a call on
393      * firePropertyChange names that specific property.  The case of
394      * the name is ignored.
395      *
396      * @since JAI 1.1
397      */
addPropertyChangeListener(String propertyName, PropertyChangeListener listener)398     public void addPropertyChangeListener(String propertyName,
399                                           PropertyChangeListener listener) {
400         eventManager.addPropertyChangeListener(propertyName, listener);
401     }
402 
403     /**
404      * Remove a PropertyChangeListener from the listener list. This
405      * removes a PropertyChangeListener that was registered for all
406      * properties.
407      *
408      * @since JAI 1.1
409      */
removePropertyChangeListener(PropertyChangeListener listener)410     public void removePropertyChangeListener(PropertyChangeListener listener) {
411         eventManager.removePropertyChangeListener(listener);
412     }
413 
414     /**
415      * Remove a PropertyChangeListener for a specific property.  The case
416      * of the name is ignored.
417      *
418      * @since JAI 1.1
419      */
removePropertyChangeListener(String propertyName, PropertyChangeListener listener)420     public void removePropertyChangeListener(String propertyName,
421                                              PropertyChangeListener listener) {
422         eventManager.removePropertyChangeListener(propertyName, listener);
423     }
424 
425     /* ----- Collection methods. ----- */
426 
427     /** Returns the number of elements in this <code>Collection</code>. */
size()428     public int size() {
429         return imageCollection.size();
430     }
431 
432     /**
433      * Returns <code>true</code> if this <code>Collection</code>
434      * contains no elements.
435      */
isEmpty()436     public boolean isEmpty() {
437         return imageCollection.isEmpty();
438     }
439 
440     /**
441      * Returns <code>true</code> if this <code>Collection</code>
442      * contains the specified object.
443      */
contains(Object o)444     public boolean contains(Object o) {
445         return imageCollection.contains(o);
446     }
447 
448     /**
449      * Returns an <code>Iterator</code> over the elements in this
450      * <code>Collection</code>.
451      */
iterator()452     public Iterator iterator() {
453         return imageCollection.iterator();
454     }
455 
456     /**
457      * Returns an array containing all of the elements in this
458      * <code>Collection</code>.
459      */
toArray()460     public Object[] toArray() {
461         return imageCollection.toArray();
462     }
463 
464     /**
465      * Returns an array containing all of the elements in this collection
466      * whose runtime type is that of the specified array.
467      *
468      * @throws ArrayStoreException if the runtime type of the specified array
469      *         is not a supertype of the runtime type of every element in this
470      *         <code>Collection</code>.
471      */
toArray(Object[] a)472     public Object[] toArray(Object[] a) {
473         return imageCollection.toArray(a);
474     }
475 
476     /**
477      * Adds the specified object to this <code>Collection</code>.
478      *
479      * @return <code>true</code> if and only if the parameter is added to the
480      *         <code>Collection</code>.
481      */
add(Object o)482     public boolean add(Object o) {
483         return imageCollection.add(o);
484     }
485 
486     /**
487      * Removes the specified object from this <code>Collection</code>.
488      *
489      * @return <code>true</code> if and only if the parameter is removed
490      *         from the <code>Collection</code>.
491      */
remove(Object o)492     public boolean remove(Object o) {
493         return imageCollection.remove(o);
494     }
495 
496     /**
497      * Returns <code>true</code> if this <code>Collection</code> contains
498      * all of the elements in the specified <code>Collection</code>.
499      */
containsAll(Collection c)500     public boolean containsAll(Collection c) {
501         return imageCollection.containsAll(c);
502     }
503 
504     /**
505      * Adds all of the elements in the specified <code>Collection</code>
506      * to this <code>Collection</code>.
507      *
508      * @return <code>true</code> if this <code>Collection</code> changed
509      * as a result of the call.
510      */
addAll(Collection c)511     public boolean addAll(Collection c) {
512         return imageCollection.addAll(c);
513     }
514 
515     /**
516      * Removes all this collection's elements that are also contained in the
517      * specified <code>Collection</code>.
518      *
519      * @return <code>true</code> if this <code>Collection</code> changed
520      * as a result of the call.
521      */
removeAll(Collection c)522     public boolean removeAll(Collection c) {
523         return imageCollection.removeAll(c);
524     }
525 
526     /**
527      * Retains only the elements in this <code>Collection</code> that are
528      * contained in the specified <code>Collection</code>.
529      *
530      * @return <code>true</code> if this <code>Collection</code> changed
531      * as a result of the call.
532      */
retainAll(Collection c)533     public boolean retainAll(Collection c) {
534         return imageCollection.retainAll(c);
535     }
536 
537     /** Removes all of the elements from this <code>Collection</code>. */
clear()538     public void clear() {
539         imageCollection.clear();
540     }
541 }
542