001: /*
002: * $RCSfile: OperationNodeSupport.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:13 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.RenderingHints;
015: import java.awt.image.Raster;
016: import java.awt.image.RenderedImage;
017: import java.awt.image.renderable.ParameterBlock;
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectOutputStream;
021: import java.io.Serializable;
022: import java.util.Hashtable;
023: import java.util.Iterator;
024: import java.util.Observable;
025: import java.util.Observer;
026: import java.util.Vector;
027: import javax.media.jai.remote.SerializableState;
028: import javax.media.jai.remote.SerializerFactory;
029:
030: /**
031: * This is a utility class that can be used by <code>OperationNode</code>s
032: * to consolidate common functionality. An instance of this class may be
033: * used as a member field of the <code>OperationNode</code> and some of the
034: * <code>OperationNode</code>'s work delegated to it.
035: *
036: * @since JAI 1.1
037: */
038: public class OperationNodeSupport implements Serializable {
039:
040: // Constants supporting compare().
041: private static final int PB_EQUAL = 0x0;
042: private static final int PB_SOURCES_DIFFER = 0x1;
043: private static final int PB_PARAMETERS_DIFFER = 0x2;
044: private static final int PB_DIFFER = PB_SOURCES_DIFFER
045: | PB_PARAMETERS_DIFFER;
046:
047: // The OperationRegistryMode.
048: private String registryModeName;
049:
050: // Critical attributes.
051: private String opName;
052: private transient OperationRegistry registry;
053: private transient ParameterBlock pb;
054: private transient RenderingHints hints;
055:
056: // Event helper.
057: private PropertyChangeSupportJAI eventManager;
058:
059: /**
060: * This instance variable is lazily constructed only when one of the
061: * PropertySource methods or one of the local property environment
062: * mutators is accessed. PropertyEnvironment is a package scope class.
063: */
064: private transient PropertyEnvironment propertySource = null;
065:
066: /**
067: * Stores local property environment modifications sequentially as
068: * a PropertyGenerator, a String, or a CopyDirective depending
069: * on which local property environment mutator method was invoked.
070: */
071: private Vector localPropEnv = new Vector();
072:
073: /**
074: * <code>Map</code> of <code>ParamObserver</code>s of instances of
075: * <code>DeferredData</code> in the parameter <code>Vector</code>.
076: */
077: private Hashtable paramObservers = new Hashtable();
078:
079: /**
080: * Compare the contents of two <code>ParameterBlock</code>s.
081: */
082: private static int compare(ParameterBlock pb1, ParameterBlock pb2) {
083: if (pb1 == null && pb2 == null) {
084: return PB_EQUAL;
085: }
086:
087: if ((pb1 == null && pb2 != null)
088: || (pb1 != null && pb2 == null)) {
089: return PB_DIFFER;
090: }
091:
092: int result = PB_EQUAL;
093: if (!equals(pb1.getSources(), pb2.getSources())) {
094: result |= PB_SOURCES_DIFFER;
095: }
096: if (!equals(pb1.getParameters(), pb2.getParameters())) {
097: result |= PB_PARAMETERS_DIFFER;
098: }
099:
100: return result;
101: }
102:
103: private static boolean equals(ParameterBlock pb1, ParameterBlock pb2) {
104: return pb1 == null ? pb2 == null : equals(pb1.getSources(), pb2
105: .getSources())
106: && equals(pb1.getParameters(), pb2.getParameters());
107: }
108:
109: private static boolean equals(Object o1, Object o2) {
110: return o1 == null ? o2 == null : o1.equals(o2);
111: }
112:
113: /**
114: * Constructs an <code>OperationNodeSupport</code> instance.
115: * All parameters except <code>opName</code> may be <code>null</code>.
116: * If non-<code>null</code> the <code>PropertyChangeSupportJAI</code>
117: * should have been created with the node as its event source (note
118: * that this cannot be verified).
119: *
120: * <p> The <code>ParameterBlock</code> may include
121: * <code>DeferredData</code> parameters. These will not be evaluated
122: * until their values are actually required, i.e., when the node is
123: * rendered. Any <code>Observable</code> events generated by such
124: * <code>DeferredData</code> parameters will be trapped by the node,
125: * converted to a <code>PropertyChangeEventJAI</code> named "parameters",
126: * and forwarded to any listeners registered with the supplied.
127: * <code>eventManager</code>. The old and new values of the event object
128: * so generated will be the previous and current values, respectively, of
129: * the data object wrapped by the <code>DeferredData</code> parameter,
130: * and thus will be instances of the class returned by the
131: * <code>getDataClass()</code> method of the <code>DeferredData</code>
132: * parameter.
133: *
134: * @param registryModeName The name of the registry mode concerned.
135: * @param opName The operation name to set.
136: * @param registry The <code>OperationRegistry</code> to set;
137: * it may be <code>null</code> in which case the registry
138: * will be set to the default JAI registry.
139: * @param pb The <code>ParameterBlock</code> to set;
140: * it may be <code>null</code>.
141: * @param hints The new <code>RenderingHints</code> to be set;
142: * it may be <code>null</code>.
143: * @param eventManager The event helper object. The property change
144: * event source of this object should be the
145: * <code>OperationNode</code> which is using the constructed
146: * <code>OperationNodeSupport</code> instance. If <code>null</code>
147: * no events will be fired.
148: *
149: * @throws IllegalArgumentException if <code>registryModeName</code>
150: * or <code>opName</code> is <code>null</code>.
151: */
152: public OperationNodeSupport(String registryModeName, String opName,
153: OperationRegistry registry, ParameterBlock pb,
154: RenderingHints hints, PropertyChangeSupportJAI eventManager) {
155: if (registryModeName == null || opName == null) {
156: throw new IllegalArgumentException(JaiI18N
157: .getString("Generic0"));
158: }
159:
160: // Set instance variables.
161: this .registryModeName = registryModeName;
162: this .opName = opName;
163: if (registry == null)
164: this .registry = JAI.getDefaultInstance()
165: .getOperationRegistry();
166: else
167: this .registry = registry;
168: this .pb = pb;
169: this .hints = hints;
170: this .eventManager = eventManager;
171:
172: // Set any DeferredData Observers.
173: if (pb != null) {
174: updateObserverMap(pb.getParameters());
175: }
176: }
177:
178: /**
179: * Class representing a copy-from-source directive set via
180: * <code>copyPropertyFromSource()</code>.
181: */
182: private class CopyDirective implements Serializable {
183: /** The name of the property. */
184: private String name;
185:
186: /** The index of the source from which to copy the property. */
187: private int index;
188:
189: /**
190: * Constructor.
191: *
192: * @param name The name of the property.
193: * @param index The index of the source from which to copy the
194: * property.
195: */
196: CopyDirective(String name, int index) {
197: if (name == null) {
198: throw new IllegalArgumentException(JaiI18N
199: .getString("Generic0"));
200: }
201: this .name = name;
202: this .index = index;
203: }
204:
205: String getName() {
206: return name;
207: }
208:
209: int getIndex() {
210: return index;
211: }
212: }
213:
214: /**
215: * Class which is an <code>Observer</code> of a <code>DeferredData</code>
216: * parameter.
217: */
218: private class ParamObserver implements Observer {
219: /** The index of the associated parameter. */
220: final int paramIndex;
221:
222: /** The <code>DeferredData</code> object to observe. */
223: final DeferredData dd;
224:
225: /**
226: * Constructor.
227: *
228: * @param paramIndex The index of the associated parameter.
229: * @param dd The <code>DeferredData</code> object to observe.
230: */
231: ParamObserver(int paramIndex, DeferredData dd) {
232: if (dd == null) {
233: throw new IllegalArgumentException(JaiI18N
234: .getString("Generic0"));
235: } else if (paramIndex < 0
236: || (pb != null && (paramIndex >= ((ParameterBlock) pb)
237: .getNumParameters()))) {
238: throw new ArrayIndexOutOfBoundsException();
239: }
240:
241: this .paramIndex = paramIndex;
242: this .dd = dd;
243:
244: // Add this object as an Observer of the Deferred Data.
245: dd.addObserver(this );
246: }
247:
248: /**
249: * Implementation of <code>Observer</code>. An update from the
250: * observed <code>DeferredData</code> causes an event to be fired
251: * if the <code>DeferredData</code> had been previously evaluated
252: * and there are event listeners.
253: */
254: public synchronized void update(Observable o, Object arg) {
255: if (!(o == dd)) {
256: return;
257: }
258:
259: // Do nothing unless the DeferredData was already evaluated.
260: if (arg != null && eventManager != null) {
261: Vector params = pb.getParameters();
262: Vector oldParams = (Vector) params.clone();
263: Vector newParams = (Vector) params.clone();
264:
265: oldParams.set(paramIndex, arg);
266: newParams.set(paramIndex, dd.getData());
267:
268: fireEvent("Parameters", oldParams, newParams);
269: }
270: }
271: }
272:
273: /**
274: * Updates the <code>Map</code> of <code>Observer</code>s of
275: * <code>DeferredData</code> instances in the parameter
276: * <code>Vector</code>.
277: */
278: private void updateObserverMap(Vector parameters) {
279: if (parameters == null) {
280: return;
281: }
282:
283: int numParameters = parameters.size();
284: for (int i = 0; i < numParameters; i++) {
285: Object parameter = parameters.get(i);
286: Integer index = new Integer(i);
287:
288: // Replace or remove ParamObserver as needed.
289: Object oldObs;
290: if (parameter instanceof DeferredData) {
291: Observer obs = new ParamObserver(i,
292: (DeferredData) parameter);
293: oldObs = paramObservers.put(index, obs);
294: } else {
295: oldObs = paramObservers.remove(index);
296: }
297:
298: // Unregister Observer from the associated DeferredData.
299: if (oldObs != null) {
300: ParamObserver obs = (ParamObserver) oldObs;
301: obs.dd.deleteObserver(obs);
302: }
303: }
304: }
305:
306: /**
307: * Returns the name of <code>RegistryMode</code> corresponding to
308: * this <code>OperationNode</code>. This value shoud be immutable
309: * for a given node. The value is returned by reference.
310: */
311: public String getRegistryModeName() {
312: return registryModeName;
313: }
314:
315: /**
316: * Returns the name of the operation the associated node represents.
317: * The value is returned by reference.
318: */
319: public String getOperationName() {
320: return opName;
321: }
322:
323: /**
324: * Sets the name of the operation the associated node represents.
325: * The value is set by reference.
326: *
327: * <p> If the operation name changes as a result of calling this
328: * method according to a case-insensitive
329: * comparison by <code>equals()</code> of the old and new names,
330: * a <code>PropertyChangeEventJAI<code> named "OperationName"
331: * will be fired by the event helper object with old and new values
332: * set to the old and new values of the operation name, respectively.
333: *
334: * @param opName The new operation name to be set.
335: *
336: * @throws IllegalArgumentException if <code>opName</code> is
337: * <code>null</code>.
338: */
339: public void setOperationName(String opName) {
340: if (opName == null) {
341: throw new IllegalArgumentException(JaiI18N
342: .getString("Generic0"));
343: }
344:
345: if (opName.equalsIgnoreCase(this .opName))
346: return;
347:
348: String oldOpName = this .opName;
349: this .opName = opName;
350: fireEvent("OperationName", oldOpName, opName);
351: resetPropertyEnvironment(false);
352: }
353:
354: /**
355: * Returns the <code>OperationRegistry</code> used by the associated
356: * node. The value is returned by reference.
357: */
358: public OperationRegistry getRegistry() {
359: return registry;
360: }
361:
362: /**
363: * Sets the <code>OperationRegistry</code> that is used by the associated
364: * node. If the specified registry is <code>null</code>, the
365: * registry will be set to the default JAI registry. The value is
366: * set by reference.
367: *
368: * <p> If the registry changes according to a direct comparison
369: * of the old and new registry references,
370: * a <code>PropertyChangeEventJAI<code> named "OperationRegistry"
371: * will be fired by the event helper object with old and new values
372: * set to the old and new values of the registry, respectively.
373: *
374: * @param registry The new <code>OperationRegistry</code> to be set;
375: * it may be <code>null</code>.
376: */
377: public void setRegistry(OperationRegistry registry) {
378: if (registry == null) {
379: registry = JAI.getDefaultInstance().getOperationRegistry();
380: }
381: if (registry != this .registry) {
382: OperationRegistry oldRegistry = this .registry;
383: this .registry = registry;
384: fireEvent("OperationRegistry", oldRegistry, registry);
385: resetPropertyEnvironment(false);
386: }
387: }
388:
389: /**
390: * Returns the <code>ParameterBlock</code> of the associated node
391: * by reference. Nodes desirous of maintaining a consistent state
392: * for their <code>ParameterBlock</code> may prefer to clone the
393: * value returned by this method.
394: */
395: public ParameterBlock getParameterBlock() {
396: return pb;
397: }
398:
399: /**
400: * Sets the <code>ParameterBlock</code> of the associated node by
401: * reference. If the specified <code>ParameterBlock</code> is
402: * <code>null</code>, it is assumed that the associated node has
403: * neither input sources nor parameters. Nodes desirous of maintaining
404: * a consistent state for their <code>ParameterBlock</code> may prefer
405: * to clone any user-supplied <code>ParameterBlock</code> before passing
406: * it to this method.
407: *
408: * <p> This method does not validate the content of the supplied
409: * <code>ParameterBlock</code>. The caller should ensure that
410: * the sources and parameters in the <code>ParameterBlock</code>
411: * are suitable for the operation the associated node represents; otherwise
412: * some form of error or exception may occur at the time of rendering.
413: *
414: * <p> If the <code>ParameterBlock</code> changes according to a
415: * comparison of the sources and parameters <code>Vector</code>s of the
416: * old and new <code>ParameterBlock</code>s using <code>equals()</code>,
417: * a <code>PropertyChangeEventJAI<code> named "ParameterBlock"
418: * will be fired by the event helper object with old and new values
419: * set to the old and new values of the <code>ParameterBlock</code>,
420: * respectively. A <code>PropertyChangeEventJAI<code> named "Sources" or
421: * "Parameters" will instead be fired if it can be determined that the
422: * <code>ParameterBlock</code> modification has affected only the sources
423: * or parameters of the node, respectively.
424: *
425: * <p> The <code>ParameterBlock</code> may include
426: * <code>DeferredData</code> parameters. These will not be evaluated
427: * until their values are actually required, i.e., when the node is
428: * rendered. Any <code>Observable</code> events generated by such
429: * <code>DeferredData</code> parameters will be trapped by the node,
430: * converted to a <code>PropertyChangeEventJAI</code> named "parameters",
431: * and forwarded to any listeners registered with the supplied.
432: * <code>eventManager</code>. The old and new values of the event object
433: * so generated will be the previous and current values, respectively, of
434: * the data object wrapped by the <code>DeferredData</code> parameter,
435: * and thus will be instances of the class returned by the
436: * <code>getDataClass()</code> method of the <code>DeferredData</code>
437: * parameter.
438: *
439: * @param pb The new <code>ParameterBlock</code> to be set;
440: * it may be <code>null</code>.
441: */
442: public void setParameterBlock(ParameterBlock pb) {
443: int comparison = compare(this .pb, pb);
444: if (comparison == PB_EQUAL) {
445: return;
446: }
447:
448: ParameterBlock oldPB = this .pb;
449: this .pb = pb;
450:
451: // Set any DeferredData Observers.
452: if (pb != null) {
453: updateObserverMap(pb.getParameters());
454: }
455:
456: if (comparison == PB_SOURCES_DIFFER) {
457: // Sources have changed.
458: fireEvent("Sources", oldPB.getSources(), pb.getSources());
459: } else if (comparison == PB_PARAMETERS_DIFFER) {
460: // Parameters have changed.
461: fireEvent("Parameters", oldPB.getParameters(), pb
462: .getParameters());
463: } else {
464: // Sources and parameters have changed.
465: fireEvent("ParameterBlock", oldPB, pb);
466: }
467:
468: resetPropertyEnvironment(false);
469: }
470:
471: /**
472: * Returns the <code>RenderingHints</code> of the associated node
473: * by reference. Nodes desirous of maintaining a consistent state
474: * for their <code>RenderingHints</code> may prefer to clone the
475: * value returned by this method.
476: */
477: public RenderingHints getRenderingHints() {
478: return hints;
479: }
480:
481: /**
482: * Sets the <code>RenderingHints</code> of the associated node. It is
483: * legal for nodes to ignore <code>RenderingHints</code> set on them by
484: * this mechanism. Nodes desirous of maintaining
485: * a consistent state for their <code>RenderingHints</code> may prefer
486: * to clone any user-supplied <code>RenderingHints</code> before passing
487: * it to this method.
488: *
489: * <p> If the <code>RenderingHints</code> changes according to a
490: * comparison by <code>equals()</code> of the old and new hints,
491: * a <code>PropertyChangeEventJAI<code> named "RenderingHints"
492: * will be fired by the event helper object with old and new values
493: * set to the old and new values of the <code>RenderingHints</code>,
494: * respectively.
495: *
496: * @param hints The new <code>RenderingHints</code> to be set;
497: * it may be <code>null</code>.
498: */
499: public void setRenderingHints(RenderingHints hints) {
500: if (equals(this .hints, hints)) {
501: return;
502: }
503: RenderingHints oldHints = this .hints;
504: this .hints = hints;
505: fireEvent("RenderingHints", oldHints, hints);
506: resetPropertyEnvironment(false);
507: }
508:
509: /**
510: * Adds a <code>PropertyGenerator</code> to the node. The property values
511: * emitted by this property generator override any previous definitions.
512: *
513: * @param pg A <code>PropertyGenerator</code> to be added to the
514: * associated node's property environment.
515: *
516: * @throws IllegalArgumentException if
517: * <code>pg</code> is <code>null</code>.
518: */
519: public void addPropertyGenerator(PropertyGenerator pg) {
520: if (pg == null) {
521: throw new IllegalArgumentException(JaiI18N
522: .getString("Generic0"));
523: }
524: localPropEnv.add(pg);
525: if (propertySource != null) {
526: propertySource.addPropertyGenerator(pg);
527: }
528: }
529:
530: /**
531: * Forces a property to be copied from the specified source node.
532: * By default, a property is copied from the first source node that
533: * that emits it. The result of specifying an invalid source is
534: * undefined.
535: *
536: * @param propertyName the name of the property to be copied.
537: * @param sourceIndex the index of the source to copy the property from.
538: * @throws IllegalArgumentException if propertyName is null.
539: */
540: public void copyPropertyFromSource(String propertyName,
541: int sourceIndex) {
542: if (propertyName == null) {
543: throw new IllegalArgumentException(JaiI18N
544: .getString("Generic0"));
545: }
546: localPropEnv.add(new CopyDirective(propertyName, sourceIndex));
547: if (propertySource != null) {
548: propertySource.copyPropertyFromSource(propertyName,
549: sourceIndex);
550: }
551: }
552:
553: /**
554: * Removes a named property from the property environment of the
555: * associated node. Unless the property is stored locally either due
556: * to having been set explicitly or to having been cached for property
557: * synchronization purposes, subsequent calls to
558: * <code>getProperty(name)</code> will return
559: * <code>java.awt.Image.UndefinedProperty</code>, and <code>name</code>
560: * will not appear on the list of properties emitted by
561: * <code>getPropertyNames()</code>.
562: *
563: * @param name A <code>String</code> naming the property to be suppressed.
564: *
565: * @throws IllegalArgumentException if
566: * <code>name</code> is <code>null</code>.
567: */
568: public void suppressProperty(String name) {
569: if (name == null) {
570: throw new IllegalArgumentException(JaiI18N
571: .getString("Generic0"));
572: }
573: localPropEnv.add(name);
574: if (propertySource != null) {
575: propertySource.suppressProperty(name);
576: }
577: }
578:
579: /**
580: * Constructs and returns a <code>PropertySource</code> suitable for
581: * use by the specified <code>OperationNode</code>. If the registry mode
582: * identified by <code>getRegistryModeName()</code> supports properties,
583: * i.e., the statement
584: * <pre>
585: * Registry.getMode(getRegistryModeName()).arePropertiesSupported()
586: * </pre>
587: * evaluates to <code>true</code>, then the <code>PropertySource</code>
588: * will include the global property environment as managed by the
589: * <code>OperationRegistry</code> for the corresponding operation.
590: * Prior and subsequent modifications to the local property environment
591: * made via this object will be reflected in the returned
592: * <code>PropertySource</code>.
593: *
594: * @param opNode the <code>OperationNode</code> requesting its
595: * <code>PropertySource</code>.
596: * @param defaultPS a <code>PropertySource</code> to be used to derive
597: * property values if and only if they would otherwise be
598: * derived by inheritance from a source rather than from a
599: * a <code>PropertyGenerator</code> or a copy-from-source
600: * directive.
601: *
602: * @throws IllegalArgumentException if opNode is null.
603: *
604: * @return A <code>PropertySource</code> including the local and, if
605: * applicable, the global property environment for the operation.
606: *
607: * @see RegistryMode
608: * @see OperationRegistry#getPropertySource(OperationNode op)
609: */
610: public PropertySource getPropertySource(OperationNode opNode,
611: PropertySource defaultPS) {
612:
613: if (opNode == null) {
614: throw new IllegalArgumentException(JaiI18N
615: .getString("Generic0"));
616: }
617:
618: if (propertySource == null) {
619: synchronized (this ) {
620: RegistryMode regMode = RegistryMode
621: .getMode(registryModeName);
622: if (regMode != null && regMode.arePropertiesSupported()) {
623: // Get the global property environment.
624: propertySource = (PropertyEnvironment) registry
625: .getPropertySource(opNode);
626: } else {
627: // This mode does not support properties so we create
628: // a default environment to permit property inheritance
629: // from the sources. The PropertyGenerators,
630: // copy-from-source directives, and suppressed properties
631: // are null.
632: propertySource = new PropertyEnvironment(
633: pb != null ? pb.getSources() : null, null,
634: null, null, opNode);
635: }
636:
637: // Update from the local environment.
638: updatePropertyEnvironment(propertySource);
639: }
640: }
641:
642: // Add the specified default source.
643: propertySource.setDefaultPropertySource(defaultPS);
644:
645: return propertySource;
646: }
647:
648: /**
649: * Resets the property environment. The list of local property
650: * environment modifications made directly on this object is reset
651: * if and only if the parameter is <code>true</code>.
652: *
653: * @param resetLocalEnvironment Whether to clear the list of property
654: * environment changes made directly on this object.
655: */
656: public void resetPropertyEnvironment(boolean resetLocalEnvironment) {
657: propertySource = null;
658: if (resetLocalEnvironment) {
659: localPropEnv.clear();
660: }
661: }
662:
663: // Add items from local environment cache.
664: private void updatePropertyEnvironment(PropertyEnvironment pe) {
665: if (pe != null) { // "pe" should never null but check anyway.
666: synchronized (this ) {
667: // Add items from the local environment.
668: int size = localPropEnv.size();
669: for (int i = 0; i < size; i++) {
670: Object element = localPropEnv.get(i);
671: if (element instanceof String) { // suppressed property
672: pe.suppressProperty((String) element);
673: } else if (element instanceof CopyDirective) {
674: CopyDirective cd = (CopyDirective) element;
675: pe.copyPropertyFromSource(cd.getName(), cd
676: .getIndex());
677: } else if (element instanceof PropertyGenerator) {
678: pe
679: .addPropertyGenerator((PropertyGenerator) element);
680: }
681: }
682: }
683: }
684: }
685:
686: private void fireEvent(String propName, Object oldVal, Object newVal) {
687: if (eventManager != null) {
688: Object eventSource = eventManager
689: .getPropertyChangeEventSource();
690: PropertyChangeEventJAI evt = new PropertyChangeEventJAI(
691: eventSource, propName, oldVal, newVal);
692: eventManager.firePropertyChange(evt);
693: }
694: }
695:
696: // Note that at present in RenderedOp and RenderableOp the only
697: // non-serializable classes handled are RenderedImage, Raster, and
698: // RenderingHints. How should this best be handled? Should an OpNode
699: // be forced to implement for example
700: //
701: // void writePB(ParameterBlock pb, ObjectOutputStream out)
702: // void ParameterBlock readPB(ObjectInputStream in)
703: //
704: // perhaps in a SerializableOperationNode?
705: // Or does this require a more generic approach using Proxy?
706:
707: /**
708: * Serializes the <code>OperationNodeSupport</code>.
709: */
710: private void writeObject(ObjectOutputStream out) throws IOException {
711: ParameterBlock pbClone = pb;
712: boolean pbCloned = false;
713:
714: // Wrap RenderedImage sources in RenderedImageStates.
715: for (int index = 0; index < pbClone.getNumSources(); index++) {
716: Object source = pbClone.getSource(index);
717: if (source != null && !(source instanceof Serializable)) {
718: if (!pbCloned) {
719: pbClone = (ParameterBlock) pb.clone();
720: pbCloned = true;
721: }
722: if (source instanceof RenderedImage) {
723: SerializableState serializableImage = SerializerFactory
724: .getState(source, null);
725: pbClone.setSource(serializableImage, index);
726: } else {
727: throw new RuntimeException(source.getClass()
728: .getName()
729: + JaiI18N
730: .getString("OperationNodeSupport0"));
731: }
732: }
733: }
734:
735: // Wrap RenderedImage parameters in RenderedImageState objects;
736: // wrap Raster parameters in RasterState objects;
737: // check other parameters for serializability.
738: for (int index = 0; index < pbClone.getNumParameters(); index++) {
739: Object parameter = pbClone.getObjectParameter(index);
740: if (parameter != null
741: && !(parameter instanceof Serializable)) {
742: if (!pbCloned) {
743: pbClone = (ParameterBlock) pb.clone();
744: pbCloned = true;
745: }
746: if (parameter instanceof Raster) {
747: pbClone.set(SerializerFactory.getState(parameter,
748: null), index);
749: } else if (parameter instanceof RenderedImage) {
750: RenderedImage ri = (RenderedImage) parameter;
751: RenderingHints hints = new RenderingHints(null);
752: hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, new Boolean(
753: true));
754: pbClone.set(SerializerFactory.getState(ri, hints),
755: index);
756: } else {
757: throw new RuntimeException(parameter.getClass()
758: .getName()
759: + JaiI18N
760: .getString("OperationNodeSupport1"));
761: }
762: }
763: }
764:
765: // Serialize the object.
766: // Write non-static and non-transient fields.
767: out.defaultWriteObject();
768: // Write ParameterBlock.
769: out.writeObject(pbClone);
770: // Write RenderingHints.
771: out.writeObject(SerializerFactory.getState(hints, null));
772: }
773:
774: /**
775: * Deserializes the <code>OperationNodeSupport</code>.
776: */
777: private synchronized void readObject(ObjectInputStream in)
778: throws IOException, ClassNotFoundException {
779:
780: // Read non-static and non-transient fields.
781: in.defaultReadObject();
782: // Read ParameterBlock.
783: pb = (ParameterBlock) in.readObject();
784: // Read RenderingHints.
785: SerializableState ss = (SerializableState) in.readObject();
786: hints = (RenderingHints) ss.getObject();
787:
788: // Wrap any RenderedImageState sources in PlanarImage objects.
789: for (int index = 0; index < pb.getNumSources(); index++) {
790: Object source = pb.getSource(index);
791: if (source instanceof SerializableState) {
792: ss = (SerializableState) source;
793: PlanarImage pi = PlanarImage
794: .wrapRenderedImage((RenderedImage) ss
795: .getObject());
796: pb.setSource(pi, index);
797: }
798: }
799:
800: // Extract Raster and PlanarImage parameters from RasterState and
801: // RenderedImageState wrappers, respectively.
802: for (int index = 0; index < pb.getNumParameters(); index++) {
803: Object parameter = pb.getObjectParameter(index);
804: if (parameter instanceof SerializableState) {
805: Object object = ((SerializableState) parameter)
806: .getObject();
807: if (object instanceof Raster)
808: pb.set(object, index);
809: else if (object instanceof RenderedImage)
810: pb.set(PlanarImage
811: .wrapRenderedImage((RenderedImage) object),
812: index);
813: else
814: pb.set(object, index);
815: }
816: }
817:
818: registry = JAI.getDefaultInstance().getOperationRegistry();
819: }
820: }
|