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.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.LinkedHashMap;
028: import java.util.ResourceBundle;
029: import java.util.Locale;
030: import java.net.URI;
031: import java.net.URISyntaxException;
032: import java.awt.image.RenderedImage;
033:
034: // JAI dependencies
035: import javax.media.jai.util.Range;
036: import javax.media.jai.EnumeratedParameter;
037: import javax.media.jai.OperationDescriptor;
038: import javax.media.jai.ParameterListDescriptor;
039: import javax.media.jai.RegistryElementDescriptor;
040: import javax.media.jai.registry.RenderedRegistryMode;
041:
042: // OpenGIS dependencies
043: import org.opengis.metadata.Identifier;
044: import org.opengis.metadata.citation.OnLineFunction;
045: import org.opengis.metadata.citation.Role;
046: import org.opengis.metadata.citation.Citation;
047: import org.opengis.metadata.citation.ResponsibleParty;
048: import org.opengis.referencing.ReferenceIdentifier;
049: import org.opengis.parameter.GeneralParameterValue;
050: import org.opengis.parameter.ParameterDescriptor;
051: import org.opengis.coverage.grid.GridCoverage;
052: import org.opengis.util.InternationalString;
053: import org.opengis.util.GenericName;
054:
055: // Geotools dependencies
056: import org.geotools.util.NameFactory;
057: import org.geotools.resources.Utilities;
058: import org.geotools.referencing.AbstractIdentifiedObject;
059: import org.opengis.parameter.InvalidParameterNameException;
060: import org.geotools.metadata.iso.citation.Citations;
061: import org.geotools.metadata.iso.citation.ContactImpl;
062: import org.geotools.metadata.iso.citation.CitationImpl;
063: import org.geotools.metadata.iso.citation.OnLineResourceImpl;
064: import org.geotools.metadata.iso.citation.ResponsiblePartyImpl;
065: import org.geotools.resources.i18n.ErrorKeys;
066: import org.geotools.resources.i18n.Errors;
067: import org.geotools.resources.XArray;
068:
069: /**
070: * Wraps a JAI's {@link ParameterListDescriptor}. This adaptor is provided for interoperability
071: * with <A HREF="http://java.sun.com/products/java-media/jai/">Java Advanced Imaging</A>. A JAI
072: * parameter list descriptor is part of an {@linkplain OperationDescriptor operation descriptor}.
073: * This adaptor make it easier to access parameters for a JAI operation through the general GeoAPI
074: * parameters framework.
075: *
076: * @since 2.2
077: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/parameter/ImagingParameterDescriptors.java $
078: * @version $Id: ImagingParameterDescriptors.java 27629 2007-10-26 09:59:20Z desruisseaux $
079: * @author Martin Desruisseaux
080: */
081: public class ImagingParameterDescriptors extends
082: DefaultParameterDescriptorGroup {
083: /**
084: * Serial number for interoperability with different versions.
085: */
086: private static final long serialVersionUID = 2127050865911951239L;
087:
088: /**
089: * Mapping between values of the "Vendor" resource (in OperationDescriptor)
090: * and the citation for know authorities.
091: */
092: private static final Object[] AUTHORITIES = { "com.sun.media.jai",
093: Citations.JAI, "org.geotools", Citations.GEOTOOLS };
094:
095: /**
096: * The default <cite>source type map</cite> as a (<code>{@linkplain RenderedImage}.class</code>,
097: * <code>{@linkplain GridCoverage}.class</code>) key-value pair. This is the default argument
098: * for wrapping a JAI operation in the
099: * {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME} registry mode.
100: */
101: public static final Map DEFAULT_SOURCE_TYPE_MAP = Collections
102: .singletonMap(RenderedImage.class, GridCoverage.class);
103:
104: /**
105: * The registry mode, usually {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME}.
106: * This field is {@code null} if {@link #operation} is null.
107: */
108: protected final String registryMode;
109:
110: /**
111: * The JAI's operation descriptor, or {@code null} if none. This is usually an
112: * instance of {@link OperationDescriptor}, but this is not strictly required.
113: */
114: protected final RegistryElementDescriptor operation;
115:
116: /**
117: * The Java Advanced Imaging parameter descriptor. If {@link #operation} is non-null, then
118: * this attribute is defined by {@link RegistryElementDescriptor#getParameterListDescriptor}.
119: */
120: protected final ParameterListDescriptor descriptor;
121:
122: /**
123: * Constructs a parameter descriptor wrapping the specified JAI operation, including sources.
124: * The {@linkplain #getName name for this parameter group} will be inferred from the
125: * {@linkplain RegistryElementDescriptor#getName name of the supplied registry element}
126: * using the {@link #properties properties} method.
127: *
128: * The <cite>source type map</cite> default to a (<code>{@linkplain RenderedImage}.class</code>,
129: * <code>{@linkplain GridCoverage}.class</code>) key-value pair and the <cite>registry
130: * mode</cite> default to {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME}.
131: *
132: * @param operation The JAI's operation descriptor, usually as an instance of
133: * {@link OperationDescriptor}.
134: */
135: public ImagingParameterDescriptors(
136: final RegistryElementDescriptor operation) {
137: this (properties(operation), operation, DEFAULT_SOURCE_TYPE_MAP,
138: RenderedRegistryMode.MODE_NAME);
139: }
140:
141: /**
142: * Constructs a parameter descriptor wrapping the specified JAI operation, including sources.
143: * The {@linkplain #getName name for this parameter group} will be inferred from the
144: * {@linkplain RegistryElementDescriptor#getName name of the supplied registry element}
145: * using the {@link #properties properties} method.
146: *
147: * The <cite>source type map</cite> default to a (<code>{@linkplain RenderedImage}.class</code>,
148: * <code>{@linkplain GridCoverage}.class</code>) key-value pair and the <cite>registry
149: * mode</cite> default to {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME}.
150: *
151: * @param operation The JAI's operation descriptor, usually as an instance of
152: * {@link OperationDescriptor}.
153: * @param extension Additional parameters to put in this descriptor, or {@code null} if none.
154: * If a parameter has the same name than an {@code operation} parameter, then the
155: * extension overrides the later.
156: *
157: * @since 2.4
158: */
159: public ImagingParameterDescriptors(
160: final RegistryElementDescriptor operation,
161: final Collection/*<ParameterDescriptor>*/extension) {
162: this (properties(operation), operation,
163: RenderedRegistryMode.MODE_NAME,
164: DEFAULT_SOURCE_TYPE_MAP, extension);
165: }
166:
167: /**
168: * Constructs a parameter descriptor wrapping the specified JAI operation, including sources.
169: * The properties map is given unchanged to the
170: * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
171: *
172: * @param properties Set of properties. Should contains at least {@code "name"}.
173: * @param operation The JAI's operation descriptor, usually as an instance of
174: * {@link OperationDescriptor}.
175: * @param sourceTypeMap Mapping from JAI source type to this group source type. Typically a
176: * singleton with the (<code>{@linkplain RenderedImage}.class</code>,
177: * <code>{@linkplain GridCoverage}.class</code>) key-value pair.
178: * @param registryMode The JAI's registry mode (usually
179: * {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME}).
180: *
181: * @deprecated Replaced by {@link #ImagingParameterDescriptors(Map,
182: * RegistryElementDescriptor,String,Map,Collection}.
183: */
184: public ImagingParameterDescriptors(final Map properties,
185: final RegistryElementDescriptor operation,
186: final Map/*<Class,Class>*/sourceTypeMap,
187: final String registryMode) {
188: this (properties, operation
189: .getParameterListDescriptor(registryMode), operation,
190: registryMode, sourceTypeMap, null);
191: }
192:
193: /**
194: * Constructs a parameter descriptor wrapping the specified JAI operation, including sources.
195: * The properties map is given unchanged to the
196: * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
197: *
198: * @param properties Set of properties. Should contains at least {@code "name"}.
199: * @param operation The JAI's operation descriptor, usually as an instance of
200: * {@link OperationDescriptor}.
201: * @param registryMode The JAI's registry mode (usually
202: * {@value javax.media.jai.registry.RenderedRegistryMode#MODE_NAME}).
203: * @param sourceTypeMap Mapping from JAI source type to this group source type. Typically a
204: * singleton with the (<code>{@linkplain RenderedImage}.class</code>,
205: * <code>{@linkplain GridCoverage}.class</code>) key-value pair.
206: * @param extension Additional parameters to put in this descriptor, or {@code null} if none.
207: * If a parameter has the same name than an {@code operation} parameter, then the
208: * extension overrides the later.
209: *
210: * @since 2.4
211: */
212: public ImagingParameterDescriptors(final Map properties,
213: final RegistryElementDescriptor operation,
214: final String registryMode,
215: final Map/*<Class,Class>*/sourceTypeMap,
216: final Collection/*<ParameterDescriptor>*/extension) {
217: this (properties, operation
218: .getParameterListDescriptor(registryMode), operation,
219: registryMode, sourceTypeMap, extension);
220: }
221:
222: /**
223: * Constructs a parameter descriptor wrapping the specified JAI parameter list descriptor.
224: * The properties map is given unchanged to the
225: * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
226: *
227: * @param properties Set of properties. Should contains at least {@code "name"}.
228: * @param descriptor The JAI descriptor.
229: */
230: public ImagingParameterDescriptors(final Map properties,
231: final ParameterListDescriptor descriptor) {
232: this (properties, descriptor, null, null, null, null);
233: }
234:
235: /**
236: * Constructs a parameter descriptor wrapping the specified JAI descriptor.
237: * If {@code operation} is non-null, then {@code descriptor} should be derived from it.
238: * This constructor is private in order to ensure this condition.
239: */
240: private ImagingParameterDescriptors(final Map properties,
241: final ParameterListDescriptor descriptor,
242: final RegistryElementDescriptor operation,
243: final String registryMode,
244: final Map/*<Class,Class>*/sourceTypeMap,
245: final Collection/*<ParameterDescriptor>*/extension) {
246: super (properties, 1, 1, asDescriptors(descriptor, operation,
247: registryMode, sourceTypeMap, extension));
248: this .descriptor = descriptor;
249: this .operation = operation;
250: this .registryMode = registryMode;
251: }
252:
253: /**
254: * Infers from the specified JAI operation a set of properties that can be given to the
255: * {@linkplain #ImagingParameterDescriptors(Map,RegistryElementDescriptor,Map,String)
256: * constructor}. The returned map includes values (when available) for the following keys:
257: * <p>
258: * <table border="1">
259: * <tr>
260: * <th nowrap>Key</th>
261: * <th nowrap>Inferred from</th>
262: * </tr>
263: * <tr>
264: * <td>{@link #NAME_KEY NAME_KEY}</td>
265: * <td>{@linkplain RegistryElementDescriptor#getName descriptor name}</td>
266: * </tr>
267: * <tr>
268: * <td>{@link #ALIAS_KEY ALIAS_KEY}</td>
269: * <td>{@code "Vendor"} (for the {@linkplain GenericName#getScope scope}) and
270: * {@code "LocalName"} {@linkplain OperationDescriptor#getResources resources}</td>
271: * </tr>
272: * <tr>
273: * <td>{@link Identifier#AUTHORITY_KEY AUTHORITY_KEY}</td>
274: * <td>{@linkplain Citations#JAI JAI} or {@linkplain Citations#GEOTOOLS Geotools}
275: * inferred from the vendor, extented with {@code "DocURL"}
276: * {@linkplain OperationDescriptor#getResources resources} as
277: * {@linkplain ResponsibleParty#getContactInfo contact information}.</td></td>
278: * </tr>
279: * <tr>
280: * <td>{@link ReferenceIdentifier#VERSION_KEY VERSION_KEY}</td>
281: * <td>{@code "Version"} {@linkplain OperationDescriptor#getResources resources}</td>
282: * </tr>
283: * <tr>
284: * <td>{@link #REMARKS_KEY REMARKS_KEY}</td>
285: * <td>{@code "Description"} {@linkplain OperationDescriptor#getResources resources}</td>
286: * </tr>
287: * </table>
288: * <p>
289: * For JAI image operation (for example {@code "Add"}, the end result is fully-qualified name
290: * like {@code "JAI:Add"} and one alias like {@code "com.sun.media.jai.Add"}.
291: * <p>
292: * This method returns a modifiable map. Users can safely changes its content in order to
293: * select for example a different name.
294: */
295: public static Map properties(
296: final RegistryElementDescriptor operation) {
297: String name = operation.getName();
298: final Map properties = new HashMap();
299: if (operation instanceof OperationDescriptor) {
300: /*
301: * Gets the vendor name (if available) using US locale in order to get something as
302: * close as possible to a kind of "locale-independent" string. This string will be
303: * used in order to remove the prefix (if any) from the global name, for example in
304: * "org.geotools.Combine" operation name. We can remove the prefix because it will
305: * appears in the GenericName's scope below (as an alias).
306: */
307: final OperationDescriptor op = (OperationDescriptor) operation;
308: final ResourceBundle bundle = op.getResourceBundle(Locale
309: .getDefault());
310: String vendor = op.getResourceBundle(Locale.US).getString(
311: "Vendor");
312: Citation authority = null;
313: if (vendor != null) {
314: vendor = vendor.trim();
315: name = ImagingParameterDescription.trimPrefix(name,
316: vendor);
317: for (int i = 0; i < AUTHORITIES.length; i += 2) {
318: if (vendor
319: .equalsIgnoreCase((String) AUTHORITIES[i])) {
320: authority = (Citation) AUTHORITIES[i + 1];
321: break;
322: }
323: }
324: }
325: /*
326: * If we are able to construct an URI, replaces the contact info for the first (and only
327: * the first) responsible party. Exactly one responsible party should be presents, since
328: * the authority is one of the hard-coded AUTHORITIES list above. We replace completely
329: * the contact info; for example we do not retain any telephone number because it would
330: * be a mismatch with the new URI purpose (this new URI do not links to information that
331: * can be used to contact the individual or organisation - it is information about an
332: * image operation, and I'm not sure that anyone wants to phone to an image operation).
333: */
334: final InternationalString description;
335: description = new ImagingParameterDescription(op,
336: "Description", null);
337: try {
338: final URI uri = new URI(bundle.getString("DocURL"));
339: final OnLineResourceImpl resource = new OnLineResourceImpl(
340: uri);
341: resource.setFunction(OnLineFunction.INFORMATION);
342: resource.setDescription(description);
343: final CitationImpl citation = new CitationImpl(
344: authority);
345: final Collection parties = citation
346: .getCitedResponsibleParties();
347: final ResponsibleParty oldParty;
348: if (true) {
349: final Iterator it = parties.iterator();
350: if (it.hasNext()) {
351: oldParty = (ResponsibleParty) it.next();
352: it.remove(); // This party will be re-injected with a new URI below.
353: } else {
354: oldParty = null;
355: }
356: }
357: final ResponsiblePartyImpl party = new ResponsiblePartyImpl(
358: oldParty);
359: party.setRole(Role.RESOURCE_PROVIDER);
360: party.setContactInfo(new ContactImpl(resource));
361: parties.add(party);
362: authority = (Citation) citation.unmodifiable();
363: } catch (URISyntaxException exception) {
364: // Invalid URI syntax. Ignore, since this property
365: // was really just for information purpose.
366: }
367: /*
368: * At this point, all properties have been created. Stores them in the map.
369: * The name should be stored as a String (not as an Identifier), otherwise
370: * the version and the authority would be ignored. For JAI image operation,
371: * the end result is fully-qualified name like "JAI:Add" and one alias like
372: * "com.sun.media.jai.Add".
373: */
374: final GenericName alias = NameFactory.create(
375: new InternationalString[] {
376: new ImagingParameterDescription(op,
377: "Vendor", null), // Scope name
378: new ImagingParameterDescription(op,
379: "LocalName", "Vendor") // Local name
380: }, '.');
381: properties.put(ALIAS_KEY, alias);
382: properties.put(REMARKS_KEY, description);
383: properties.put(ReferenceIdentifier.VERSION_KEY, bundle
384: .getString("Version"));
385: properties.put(Identifier.AUTHORITY_KEY, authority);
386: }
387: properties.put(NAME_KEY, name);
388: return properties;
389: }
390:
391: /**
392: * Returns the JAI's parameters as {@link ParameterDescriptor} objects.
393: * This method is a work around for RFE #4093999 in Sun's bug database
394: * ("Relax constraint on placement of this()/super() call in constructors").
395: */
396: private static ParameterDescriptor[] asDescriptors(
397: final ParameterListDescriptor descriptor,
398: final RegistryElementDescriptor operation,
399: final String registryMode,
400: final Map/*<Class,Class>*/sourceTypeMap,
401: final Collection/*<ParameterDescriptor>*/extension) {
402: /*
403: * Creates the list of JAI descriptor to be replaced by user-supplied parameters.
404: * Note that this map will be modified again in the remaining of this method.
405: */
406: ensureNonNull("descriptor", descriptor);
407: final Map/*<String,ParameterDescriptor>*/replacements = new LinkedHashMap();
408: if (extension != null) {
409: for (final Iterator it = extension.iterator(); it.hasNext();) {
410: final ParameterDescriptor d = (ParameterDescriptor) it
411: .next();
412: final String name = d.getName().getCode().trim()
413: .toLowerCase();
414: if (replacements.put(name, d) != null) {
415: throw new InvalidParameterNameException(Errors
416: .format(ErrorKeys.DUPLICATED_VALUES_$1,
417: name), name);
418: }
419: }
420: }
421: /*
422: * JAI considers sources as a special kind of parameters, while GridCoverageProcessor makes
423: * no distinction. If the registry element "operation" is really a JAI's OperationDescriptor
424: * (which should occurs most of the time), prepend the JAI's sources before all ordinary
425: * parameters. In addition, transforms the source type if needed.
426: */
427: final int numSources;
428: final int numParameters = descriptor.getNumParameters();
429: final Map/*<String,CharSequence>*/properties = new HashMap();
430: ParameterDescriptor[] desc;
431: if (operation instanceof OperationDescriptor) {
432: final OperationDescriptor op = (OperationDescriptor) operation;
433: final String[] names = op.getSourceNames();
434: final Class[] types = op.getSourceClasses(registryMode);
435: numSources = op.getNumSources();
436: desc = new ParameterDescriptor[numParameters + numSources];
437: for (int i = 0; i < numSources; i++) {
438: Class type = (Class) sourceTypeMap.get(types[i]);
439: if (type == null) {
440: type = types[i];
441: }
442: String name = names[i];
443: properties.clear();
444: if (numSources == 1) {
445: /*
446: * If there is only one source argument, rename for example "Source0"
447: * as "Source" for better compliance with OpenGIS usage. However, we
448: * will keep the original name as an alias.
449: */
450: final int length = name.length();
451: if (length != 0) {
452: final char c = name.charAt(length - 1);
453: if (c == '0' || c == '1') {
454: properties.put(ALIAS_KEY, name);
455: name = name.substring(0, length - 1);
456: }
457: }
458: }
459: properties.put(NAME_KEY, name);
460: desc[i] = new DefaultParameterDescriptor(properties,
461: type, null, // validValues
462: null, // defaultValue
463: null, // minimum
464: null, // maximum
465: null, // unit
466: true); // required
467: }
468: } else {
469: numSources = 0;
470: desc = new ParameterDescriptor[numParameters];
471: }
472: /*
473: * Source parameters completed. Now get the ordinary parameters. We check for
474: * replacement of JAI parameters by user-supplied parameters in this process.
475: */
476: final String[] names = descriptor.getParamNames();
477: final Class[] classes = descriptor.getParamClasses();
478: final Object[] defaults = descriptor.getParamDefaults();
479: for (int i = 0; i < numParameters; i++) {
480: final String name = names[i];
481: final ParameterDescriptor replacement = (ParameterDescriptor) replacements
482: .remove(name.trim().toLowerCase());
483: if (replacement != null) {
484: desc[i + numSources] = replacement;
485: continue;
486: }
487: final Class type = classes[i];
488: final Range range = descriptor.getParamValueRange(name);
489: final Comparable min, max;
490: if (range != null) {
491: min = range.getMinValue();
492: max = range.getMaxValue();
493: } else {
494: min = null;
495: max = null;
496: }
497: EnumeratedParameter[] validValues;
498: if (EnumeratedParameter.class.isAssignableFrom(type))
499: try {
500: validValues = descriptor
501: .getEnumeratedParameterValues(name);
502: } catch (UnsupportedOperationException exception) {
503: validValues = null;
504: }
505: else {
506: validValues = null;
507: }
508: Object defaultValue = defaults[i];
509: if (defaultValue == ParameterListDescriptor.NO_PARAMETER_DEFAULT) {
510: defaultValue = null;
511: }
512: properties.clear();
513: properties.put(NAME_KEY, name);
514: if (operation instanceof OperationDescriptor) {
515: final ImagingParameterDescription remark = new ImagingParameterDescription(
516: (OperationDescriptor) operation, i);
517: if (remark.exists()) {
518: properties.put(REMARKS_KEY, remark);
519: }
520: }
521: desc[i + numSources] = new DefaultParameterDescriptor(
522: properties, type, validValues, defaultValue, min,
523: max, null, true);
524: }
525: /*
526: * Appends the remaining extra descriptors. Note that some descriptor may
527: * have been removed from the 'replacements' map before we reach this point.
528: */
529: int i = desc.length;
530: desc = (ParameterDescriptor[]) XArray.resize(desc, i
531: + replacements.size());
532: for (final Iterator it = replacements.values().iterator(); it
533: .hasNext();) {
534: desc[i++] = (ParameterDescriptor) it.next();
535: }
536: return desc;
537: }
538:
539: /**
540: * Creates a new instance of parameter value group. A JAI {@link javax.media.jai.ParameterList}
541: * is created for holding parameter values, and wrapped into an {@link ImagingParameters}
542: * instance.
543: */
544: public GeneralParameterValue createValue() {
545: return new ImagingParameters(this );
546: }
547:
548: /**
549: * Compares the specified object with this parameter group for equality.
550: *
551: * @param object The object to compare to {@code this}.
552: * @param compareMetadata {@code true} for performing a strict comparaison, or
553: * {@code false} for comparing only properties relevant to transformations.
554: * @return {@code true} if both objects are equal.
555: */
556: public boolean equals(final AbstractIdentifiedObject object,
557: final boolean compareMetadata) {
558: if (object == this ) {
559: // Slight optimization
560: return true;
561: }
562: if (super .equals(object, compareMetadata)) {
563: final ImagingParameterDescriptors that = (ImagingParameterDescriptors) object;
564: return Utilities.equals(this .operation, that.operation)
565: && Utilities.equals(this .descriptor,
566: that.descriptor);
567: }
568: return false;
569: }
570:
571: /**
572: * Returns a hash value for this parameter. This value doesn't need
573: * to be the same in past or future versions of this class.
574: */
575: public int hashCode() {
576: return super.hashCode() ^ descriptor.hashCode();
577: }
578: }
|