001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2005, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.parameter;
018:
019: // J2SE dependencies
020: import java.util.Map;
021: import java.util.Set;
022: import java.util.List;
023: import java.util.Arrays;
024: import java.lang.reflect.Field;
025: import java.lang.reflect.Method;
026:
027: // JAI dependencies
028: import javax.media.jai.util.Range;
029: import javax.media.jai.ParameterList;
030: import javax.media.jai.ParameterListDescriptor;
031: import javax.media.jai.ParameterListImpl;
032: import javax.media.jai.ParameterBlockJAI;
033: import javax.media.jai.OperationDescriptor;
034: import javax.media.jai.EnumeratedParameter;
035:
036: // OpenGIS dependencies
037: import org.opengis.parameter.ParameterValue;
038: import org.opengis.parameter.ParameterValueGroup;
039: import org.opengis.parameter.ParameterDescriptor;
040: import org.opengis.parameter.ParameterDescriptorGroup;
041: import org.opengis.parameter.ParameterNotFoundException;
042: import org.opengis.parameter.InvalidParameterNameException;
043:
044: // Geotools dependencies
045: import org.geotools.resources.Utilities;
046: import org.geotools.resources.i18n.Errors;
047: import org.geotools.resources.i18n.ErrorKeys;
048: import org.geotools.resources.UnmodifiableArrayList;
049: import org.geotools.referencing.AbstractIdentifiedObject;
050:
051: /**
052: * Wraps a JAI's {@link ParameterList}. Any change to a {@linkplain #parameter parameter value}
053: * in this group is reflected into the {@linkplain #parameters underlying parameter list}, and
054: * conversely. This adaptor is provided for interoperability with
055: * <A HREF="http://java.sun.com/products/java-media/jai/">Java Advanced Imaging</A>.
056: * A typical usage is to wrap a JAI {@linkplain OperationDescriptor operation descriptor} into an
057: * {@linkplain ImagingParameterDescriptors imaging parameter descriptor} and create instances of
058: * {@code ImagingParameters} through the {@link ImagingParameterDescriptors#createValue createValue}
059: * method.
060: *
061: * @since 2.2
062: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/parameter/ImagingParameters.java $
063: * @version $Id: ImagingParameters.java 26799 2007-08-31 21:31:21Z desruisseaux $
064: * @author Martin Desruisseaux
065: */
066: public class ImagingParameters extends AbstractParameter implements
067: ParameterValueGroup {
068: /**
069: * Serial number for interoperability with different versions.
070: */
071: private static final long serialVersionUID = 1378692626023992530L;
072:
073: /**
074: * The JAI's parameter list. This is also the backing store for this
075: * {@linkplain ParameterValueGroup parameter value group}: all "ordinary" parameters
076: * (i.e. <strong>not</strong> including {@linkplain ParameterBlockJAI#getSources sources})
077: * are actually stored in this list.
078: * <P>
079: * If the {@linkplain ImagingParameterDescriptors#descriptor JAI descriptor} is an instance
080: * of {@link OperationDescriptor}, then this parameter list is also an instance of
081: * {@link ParameterBlockJAI}. The {@linkplain ParameterBlockJAI#getSources sources}
082: * must be handled separatly, because the source type for a JAI operator (typically
083: * {@link java.awt.image.RenderedImage}) is not the same than the source type for a
084: * coverage operation (typically {@link org.opengis.coverage.GridCoverage}).
085: */
086: public final ParameterList parameters;
087:
088: /**
089: * The wrappers around each elements in {@link #parameters} as an immutable list.
090: * Will be created by {@link #createElements} only when first needed. Note that
091: * while this list may be immutable, <strong>elements</strong> in this list stay
092: * modifiable. The goal is to allows the following idiom:
093: *
094: * <blockquote><pre>
095: * values().get(i).setValue(myValue);
096: * </pre></blockquote>
097: */
098: private List/*<ParameterValue>*/values;
099:
100: /**
101: * Constructs a parameter group for the specified descriptor.
102: */
103: public ImagingParameters(
104: final ImagingParameterDescriptors descriptor) {
105: super (descriptor);
106: if (descriptor.operation instanceof OperationDescriptor) {
107: // Parameters with sources
108: parameters = new ParameterBlockJAI(
109: (OperationDescriptor) descriptor.operation,
110: descriptor.registryMode);
111: } else {
112: // Parameters without sources
113: parameters = new ParameterListImpl(descriptor.descriptor);
114: }
115: }
116:
117: /**
118: * Constructs a parameter group wrapping the specified JAI parameters.
119: * A default {@link ImagingParameterDescriptors} is created.
120: *
121: * @param properties Set of properties. Should contains at least {@code "name"}.
122: * @param parameters The JAI's parameters.
123: */
124: public ImagingParameters(final Map properties,
125: final ParameterList parameters) {
126: super (new ImagingParameterDescriptors(properties, parameters
127: .getParameterListDescriptor()));
128: this .parameters = parameters;
129: ensureNonNull("parameters", parameters);
130: }
131:
132: /**
133: * Returns {@code true} if the specified OGC descriptor is compatible with the specified
134: * JAI descriptor. Note that the JAI descriptor is allowed to be less strict than the OGC
135: * one. This is okay because {@link ImagingParameter} will keep a reference to the stricter
136: * OGC descriptor, which can be used for performing a strict check if we wish.
137: *
138: * @param descriptor
139: * The OGC descriptor.
140: * @param listDescriptor
141: * The JAI descriptor.
142: * @param names
143: * The array returned by {@code listDescriptor.getParamNames()},
144: * obtained once for ever by the caller for efficienty.
145: * @param types
146: * The array returned by {@code listDescriptor.getParamClasses()},
147: * obtained once for ever by the caller for efficienty.
148: * @param names
149: * The array returned by {@code listDescriptor.getEnumeratedParameterNames()},
150: * obtained once for ever by the caller for efficienty.
151: */
152: private static boolean compatible(
153: final ParameterDescriptor descriptor,
154: final ParameterListDescriptor listDescriptor,
155: final String[] names, final Class[] types,
156: final String[] enumerated) {
157: final String name = descriptor.getName().getCode().trim();
158: Class type = null;
159: if (names != null) {
160: for (int i = 0; i < names.length; i++) {
161: if (name.equalsIgnoreCase(names[i])) {
162: type = types[i];
163: break;
164: }
165: }
166: }
167: if (type == null
168: || !type.isAssignableFrom(descriptor.getValueClass())) {
169: return false;
170: }
171: final Range range = listDescriptor.getParamValueRange(name);
172: if (range != null) {
173: Comparable c;
174: c = descriptor.getMinimumValue();
175: if (c != null && !range.contains(c)) {
176: return false;
177: }
178: c = descriptor.getMaximumValue();
179: if (c != null && !range.contains(c)) {
180: return false;
181: }
182: }
183: if (enumerated != null) {
184: for (int i = 0; i < enumerated.length; i++) {
185: if (name.equalsIgnoreCase(enumerated[i])) {
186: final EnumeratedParameter[] restrictions;
187: restrictions = listDescriptor
188: .getEnumeratedParameterValues(name);
189: final Set valids = descriptor.getValidValues();
190: if (valids == null
191: || !Arrays.asList(restrictions)
192: .containsAll(valids)) {
193: return false;
194: }
195: }
196: }
197: }
198: return true;
199: }
200:
201: /**
202: * Creates and fills the {@link #values} list. Note: this method must creates elements
203: * inconditionnally and most not requires synchronization for proper working of the
204: * {@link #clone} method.
205: *
206: * @return The array which is backing {@link #values}. This array is returned only in order
207: * to allow {@link #clone} to modify the values right after the clone. In other cases,
208: * this array should be discarted.
209: */
210: private ParameterValue[] createElements() {
211: final ImagingParameterDescriptors descriptor = (ImagingParameterDescriptors) this .descriptor;
212: final ParameterListDescriptor listDescriptor = parameters
213: .getParameterListDescriptor();
214: final String[] names = listDescriptor.getParamNames();
215: final Class[] types = listDescriptor.getParamClasses();
216: final String[] enumerated = listDescriptor
217: .getEnumeratedParameterNames();
218: final List descriptors = descriptor.descriptors();
219: final ParameterValue[] values = new ParameterValue[descriptors
220: .size()];
221: for (int i = 0; i < values.length; i++) {
222: final ParameterDescriptor d = (ParameterDescriptor) descriptors
223: .get(i);
224: final ParameterValue value;
225: if (compatible(d, listDescriptor, names, types, enumerated)) {
226: /*
227: * Found a parameter which is a member of the JAI ParameterList, and the
228: * type matches the expected one. Uses 'parameters' as the backing store.
229: */
230: value = new ImagingParameter(d, parameters);
231: } else {
232: /*
233: * In theory, we should use ParameterBlock sources. However, we can't because
234: * the type is not the same: JAI operations typically expect a RenderedImage
235: * source, while coverage operations typically expect a GridCoverage source.
236: * The value will be stored separatly, and the coverage framework will need
237: * to handle it itself.
238: */
239: value = (ParameterValue) d.createValue();
240: }
241: values[i] = value;
242: }
243: /*
244: * Checks for name clashes.
245: */
246: for (int j = 0; j < values.length; j++) {
247: final String name = values[j].getDescriptor().getName()
248: .getCode().trim();
249: for (int i = 0; i < values.length; i++) {
250: if (i != j) {
251: final ParameterDescriptor d = (ParameterDescriptor) values[i]
252: .getDescriptor();
253: if (AbstractIdentifiedObject.nameMatches(d, name)) {
254: throw new InvalidParameterNameException(
255: Errors
256: .format(
257: ErrorKeys.PARAMETER_NAME_CLASH_$4,
258: d.getName().getCode(),
259: new Integer(j), // The duplicated name
260: name, new Integer(i)), // The existing name
261: name);
262: }
263: }
264: }
265: }
266: this .values = new UnmodifiableArrayList(values);
267: return values;
268: }
269:
270: /**
271: * Returns all values in this group as an unmodifiable list. The returned list contains all
272: * parameters found in the {@linkplain #parameters underlying parameter list}. In addition, it
273: * may contains sources found in the JAI's {@linkplain OperationDescriptor operation descriptor}.
274: */
275: public synchronized List values() {
276: if (values == null) {
277: createElements();
278: }
279: assert ((ParameterDescriptorGroup) descriptor).descriptors()
280: .size() == values.size() : values;
281: return values;
282: }
283:
284: /**
285: * Returns the value in this group for the specified identifier code. Getter and setter methods
286: * will use directly the JAI's {@linkplain #parameters parameter list} as the underlying backing
287: * store, when applicable.
288: *
289: * @param name The case insensitive identifier code of the parameter to search for.
290: * @return The parameter value for the given identifier code.
291: * @throws ParameterNotFoundException if there is no parameter value for the given identifier code.
292: */
293: public synchronized ParameterValue parameter(String name)
294: throws ParameterNotFoundException {
295: ensureNonNull("name", name);
296: name = name.trim();
297: final List values = values();
298: final int size = values.size();
299: for (int i = 0; i < size; i++) {
300: final ParameterValue value = (ParameterValue) values.get(i);
301: if (AbstractIdentifiedObject.nameMatches(value
302: .getDescriptor(), name)) {
303: return value;
304: }
305: }
306: throw new ParameterNotFoundException(Errors.format(
307: ErrorKeys.MISSING_PARAMETER_$1, name), name);
308: }
309:
310: /**
311: * Always throws an exception, since JAI's {@linkplain ParameterList parameter list}
312: * don't have subgroups.
313: */
314: public List groups(final String name)
315: throws ParameterNotFoundException {
316: throw new ParameterNotFoundException(Errors.format(
317: ErrorKeys.MISSING_PARAMETER_$1, name), name);
318: }
319:
320: /**
321: * Always throws an exception, since JAI's {@linkplain ParameterList parameter list}
322: * don't have subgroups.
323: */
324: public ParameterValueGroup addGroup(final String name)
325: throws ParameterNotFoundException, IllegalStateException {
326: throw new ParameterNotFoundException(Errors.format(
327: ErrorKeys.MISSING_PARAMETER_$1, name), name);
328: }
329:
330: /**
331: * Compares the specified object with this parameter group for equality.
332: */
333: public boolean equals(final Object object) {
334: if (object == this ) {
335: // Slight optimization
336: return true;
337: }
338: if (super .equals(object)) {
339: final ImagingParameters that = (ImagingParameters) object;
340: return Utilities.equals(this .parameters, that.parameters);
341: }
342: return false;
343: }
344:
345: /**
346: * Returns a hash value for this parameter group. This value doesn't need
347: * to be the same in past or future versions of this class.
348: */
349: public int hashCode() {
350: return super .hashCode() * 37 + parameters.hashCode();
351: }
352:
353: /**
354: * Returns a deep copy of this group of parameter values.
355: */
356: public synchronized Object clone() {
357: final ImagingParameters copy = (ImagingParameters) super
358: .clone();
359: try {
360: final Method cloneMethod = parameters.getClass().getMethod(
361: "clone", (Class[]) null);
362: final Field paramField = ImagingParameters.class
363: .getField("parameters");
364: paramField.setAccessible(true); // Will work only with J2SE 1.5 or above.
365: paramField.set(copy, cloneMethod.invoke(parameters,
366: (Object[]) null));
367: } catch (Exception exception) {
368: // TODO: localize.
369: // TODO: Use constructor with Throwable when we will be allowed to compile for J2SE 1.5.
370: UnsupportedOperationException e = new UnsupportedOperationException(
371: "Clone not supported.");
372: e.initCause(exception);
373: throw e;
374: }
375: /*
376: * Most elements in the values list are ImagingParameter instances, which are backed by the
377: * ParameterList. If the list was already created, we need to overwrite it with a new list
378: * filled with ImagingParameter instances that reference the cloned ParameterList. The call
379: * to createElements() do this job while preserving the parameter values since we cloned the
380: * ParameterList first.
381: *
382: * We can not just set the list to null and wait for it to be lazily created, because not
383: * all elements are ImagingParameter instances. Those that are not need to be cloned.
384: */
385: if (copy.values != null) {
386: final ParameterValue[] cloned = copy.createElements();
387: assert values.size() == cloned.length : values;
388: for (int i = 0; i < cloned.length; i++) {
389: if (!(cloned[i] instanceof ImagingParameter)) {
390: cloned[i] = (ParameterValue) ((ParameterValue) values
391: .get(i)).clone();
392: }
393: }
394: }
395: return copy;
396: }
397: }
|