001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2004, 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: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.parameter;
021:
022: // J2SE dependencies
023: import java.util.ArrayList;
024: import java.util.Collections;
025: import java.util.Iterator;
026: import java.util.LinkedHashMap;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Set;
030:
031: // OpenGIS dependencies
032: import org.opengis.metadata.Identifier; // For javadoc
033: import org.opengis.parameter.GeneralParameterDescriptor;
034: import org.opengis.parameter.GeneralParameterValue;
035: import org.opengis.parameter.InvalidParameterCardinalityException;
036: import org.opengis.parameter.InvalidParameterTypeException;
037: import org.opengis.parameter.ParameterDescriptor;
038: import org.opengis.parameter.ParameterDescriptorGroup;
039: import org.opengis.parameter.ParameterNotFoundException;
040: import org.opengis.parameter.ParameterValue;
041: import org.opengis.parameter.ParameterValueGroup;
042:
043: // Geotools dependencies
044: import org.geotools.referencing.AbstractIdentifiedObject;
045: import org.geotools.resources.i18n.ErrorKeys;
046: import org.geotools.resources.i18n.Errors;
047: import org.geotools.resources.Utilities;
048:
049: /**
050: * A group of related parameter values. The same group can be repeated more than once in an
051: * {@linkplain org.opengis.referencing.operation.Operation operation} or higher level
052: * {@link ParameterValueGroup}, if those instances contain different
053: * values of one or more {@link ParameterValue}s which suitably distinquish among
054: * those groups.
055: *
056: * @since 2.1
057: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/parameter/ParameterGroup.java $
058: * @version $Id: ParameterGroup.java 25262 2007-04-23 21:11:16Z desruisseaux $
059: * @author Martin Desruisseaux
060: * @author Jody Garnett (Refractions Research)
061: *
062: * @see DefaultParameterDescriptorGroup
063: * @see Parameter
064: */
065: public class ParameterGroup extends AbstractParameter implements
066: ParameterValueGroup {
067: /**
068: * Serial number for interoperability with different versions.
069: */
070: private static final long serialVersionUID = -1985309386356545126L;
071:
072: /**
073: * An empty parameter value group. This group contains no parameter value.
074: */
075: public static ParameterValueGroup EMPTY = new ParameterGroup(
076: Collections.singletonMap(ParameterDescriptorGroup.NAME_KEY,
077: "Void"), new ParameterValue[0]);
078:
079: /**
080: * The {@linkplain #values() parameter values} for this group.
081: * Note: consider as final. This field is not final only in order
082: * to allows {@link #clone} to work.
083: */
084: private ArrayList/*<GeneralParameterValue>*/values;
085:
086: /**
087: * A view of {@link #values} as an immutable list. Will be constructed only when first
088: * needed. Note that while this list may be immutable, <strong>elements</strong> in this
089: * list stay modifiable. The goal is to allows the following idiom:
090: *
091: * <blockquote><pre>
092: * values().get(i).setValue(myValue);
093: * </pre></blockquote>
094: */
095: private transient List asList;
096:
097: /**
098: * Constructs a parameter group from the specified descriptor.
099: * All {@linkplain #values parameter values} will be initialized
100: * to their default value.
101: *
102: * @param descriptor The descriptor for this group.
103: */
104: public ParameterGroup(final ParameterDescriptorGroup descriptor) {
105: super (descriptor);
106: final List/*<GeneralParameterDescriptor>*/parameters = descriptor
107: .descriptors();
108: values = new ArrayList/*<GeneralParameterValue>*/(parameters
109: .size());
110: for (final Iterator it = parameters.iterator(); it.hasNext();) {
111: final GeneralParameterDescriptor element = (GeneralParameterDescriptor) it
112: .next();
113: for (int count = element.getMinimumOccurs(); --count >= 0;) {
114: final GeneralParameterValue value = element
115: .createValue();
116: ensureNonNull("createValue", value);
117: values.add(value);
118: }
119: }
120: }
121:
122: /**
123: * Constructs a parameter group from the specified descriptor and list of parameters.
124: *
125: * @param descriptor The descriptor for this group.
126: * @param values The list of parameter values.
127: *
128: * @throws IllegalStateException if the number of {@linkplain ParameterValue parameter}
129: * occurences doesn't matches the number declared in the
130: * {@linkplain ParameterDescriptorGroup descriptor}.
131: */
132: public ParameterGroup(final ParameterDescriptorGroup descriptor,
133: final GeneralParameterValue[] values) {
134: super (descriptor);
135: ensureNonNull("values", values);
136: this .values = new ArrayList(values.length);
137: for (int i = 0; i < values.length; i++) {
138: this .values.add(values[i]);
139: }
140: final List/*<GeneralParameterDescriptor>*/parameters = descriptor
141: .descriptors();
142: final Map occurences = new LinkedHashMap(Math.round(parameters
143: .size() / 0.75f) + 1, 0.75f);
144: for (final Iterator it = parameters.iterator(); it.hasNext();) {
145: final GeneralParameterDescriptor param = (GeneralParameterDescriptor) it
146: .next();
147: ensureNonNull("parameters", param);
148: occurences.put(param, new int[1]);
149: // The value 'int[1]' will be used by 'ensureValidOccurs'
150: }
151: ensureValidOccurs(values, occurences);
152: }
153:
154: /**
155: * Constructs a parameter group from the specified list of parameters.
156: *
157: * @param properties The properties for the
158: * {@linkplain DefaultParameterDescriptorGroup operation parameter group}
159: * to construct from the list of parameters.
160: * @param values The list of parameter values.
161: *
162: * @throws IllegalStateException if the number of {@linkplain ParameterValue parameter}
163: * occurences doesn't matches the number declared in the
164: * {@linkplain ParameterDescriptorGroup descriptor}.
165: */
166: public ParameterGroup(final Map properties,
167: final GeneralParameterValue[] values) {
168: super (createDescriptor(properties, values));
169: this .values = new ArrayList(values.length);
170: for (int i = 0; i < values.length; i++) {
171: this .values.add(values[i]);
172: }
173: }
174:
175: /**
176: * Work around for RFE #4093999 in Sun's bug database
177: * ("Relax constraint on placement of this()/super() call in constructors").
178: *
179: * @throws IllegalStateException if the number of {@linkplain ParameterValue parameter}
180: * occurences doesn't matches the number declared in the
181: * {@linkplain ParameterDescriptorGroup descriptor}.
182: */
183: private static ParameterDescriptorGroup createDescriptor(
184: final Map properties, final GeneralParameterValue[] values) {
185: ensureNonNull("values", values);
186: final Map occurences = new LinkedHashMap(Math
187: .round(values.length / 0.75f) + 1, 0.75f);
188: for (int i = 0; i < values.length; i++) {
189: ensureNonNull("values", values, i);
190: occurences.put(values[i].getDescriptor(), new int[1]);
191: // The value 'int[1]' will be used by 'ensureValidOccurs'
192: }
193: ensureValidOccurs(values, occurences);
194: final Set descriptors = occurences.keySet();
195: return new DefaultParameterDescriptorGroup(
196: properties,
197: (GeneralParameterDescriptor[]) descriptors
198: .toArray(new GeneralParameterDescriptor[descriptors
199: .size()]));
200: }
201:
202: /**
203: * Make sure that the number of occurences of each values is inside the expected range.
204: *
205: * @param values The list of parameter values.
206: * @param occurences A map of the number of occurences of a value for each descriptor.
207: * The key must be {@link GeneralParameterDescriptor} instances and the values
208: * must be {@code int[]} array of length 1 initialized with the 0 value.
209: *
210: * @throws IllegalStateException if the number of {@linkplain ParameterValue parameter}
211: * occurences doesn't matches the number declared in the
212: * {@linkplain ParameterDescriptorGroup descriptor}.
213: */
214: private static void ensureValidOccurs(
215: final GeneralParameterValue[] values, final Map occurences) {
216: /*
217: * Count the parameters occurences.
218: */
219: for (int i = 0; i < values.length; i++) {
220: ensureNonNull("values", values, i);
221: final GeneralParameterDescriptor descriptor = values[i]
222: .getDescriptor();
223: final int[] count = (int[]) occurences.get(descriptor);
224: if (count == null) {
225: final String name = getName(descriptor);
226: throw new InvalidParameterTypeException(Errors.format(
227: ErrorKeys.ILLEGAL_DESCRIPTOR_FOR_PARAMETER_$1,
228: name), name);
229: }
230: count[0]++;
231: }
232: /*
233: * Now check if the occurences are in the expected ranges.
234: */
235: for (final Iterator it = occurences.entrySet().iterator(); it
236: .hasNext();) {
237: final Map.Entry entry = (Map.Entry) it.next();
238: final GeneralParameterDescriptor descriptor = (GeneralParameterDescriptor) entry
239: .getKey();
240: final int count = ((int[]) entry.getValue())[0];
241: final int min = descriptor.getMinimumOccurs();
242: final int max = descriptor.getMaximumOccurs();
243: if (!(count >= min && count <= max)) {
244: final String name = getName(descriptor);
245: throw new InvalidParameterCardinalityException(
246: Errors
247: .format(
248: ErrorKeys.ILLEGAL_OCCURS_FOR_PARAMETER_$4,
249: name, new Integer(count),
250: new Integer(min), new Integer(
251: max)), name);
252: }
253: }
254: }
255:
256: /**
257: * Returns the values in this group. Changes in this list are reflected on this
258: * {@code ParameterValueGroup}. The returned list supports the
259: * {@link List#add(Object) add} operation.
260: */
261: public List values() {
262: if (asList == null) {
263: asList = new ParameterValueList(
264: (ParameterDescriptorGroup) descriptor, values);
265: }
266: return asList;
267: }
268:
269: /**
270: * Returns the parameter value at the specified index.
271: *
272: * @param index The zero-based index.
273: * @return The parameter value at the specified index.
274: * @throws IndexOutOfBoundsException if the specified index is out of bounds.
275: */
276: final GeneralParameterValue parameter(final int index)
277: throws IndexOutOfBoundsException {
278: return (GeneralParameterValue) values.get(index); // Remove cast with J2SE 1.5.
279: }
280:
281: /**
282: * Returns the value in this group for the specified
283: * {@linkplain Identifier#getCode identifier code}.
284: * If no {@linkplain ParameterValue parameter value} is found but
285: * a {@linkplain ParameterDescriptor parameter descriptor} is found
286: * (which may occurs if the parameter is optional, i.e.
287: * <code>{@linkplain ParameterDescriptor#getMinimumOccurs minimumOccurs} == 0</code>),
288: * then a {@linkplain ParameterValue parameter value} is
289: * automatically created and initialized to its
290: * {@linkplain ParameterDescriptor#getDefaultValue default value} (if any).
291: *
292: * <P>This convenience method provides a way to get and set parameter values by name. For
293: * example the following idiom fetches a floating point value for the
294: * <code>"false_easting"</code> parameter:</P>
295: *
296: * <blockquote><code>
297: * double value =
298: * parameter("false_easting").{@linkplain ParameterValue#doubleValue() doubleValue()};
299: * </code></blockquote>
300: *
301: * <P>This method do not search recursively in subgroups. This is because more than one
302: * subgroup may exist for the same {@linkplain ParameterDescriptorGroup descriptor}. The
303: * user must {@linkplain #groups query all subgroups} and select explicitly
304: * the appropriate one to use.</P>
305: *
306: * @param name The case insensitive {@linkplain Identifier#getCode identifier code} of the
307: * parameter to search for.
308: * @return The parameter value for the given identifier code.
309: * @throws ParameterNotFoundException if there is no parameter value for the given identifier
310: * code.
311: */
312: public ParameterValue parameter(String name)
313: throws ParameterNotFoundException {
314: ensureNonNull("name", name);
315: name = name.trim();
316: for (final Iterator it = values.iterator(); it.hasNext();) {
317: final GeneralParameterValue value = (GeneralParameterValue) it
318: .next();
319: if (value instanceof ParameterValue) {
320: if (AbstractIdentifiedObject.nameMatches(value
321: .getDescriptor(), name)) {
322: return (ParameterValue) value;
323: }
324: }
325: }
326: /*
327: * No existing parameter found. Check if an optional parameter exists.
328: * If such a descriptor is found, create it, add it to the list of values
329: * and returns it.
330: */
331: // TODO: The following lines should be considerably shorter with J2SE 1.5:
332: // for (GeneralParameterDescriptor descriptor : getDescriptor().descriptors()) {
333: for (final Iterator it = ((ParameterDescriptorGroup) getDescriptor())
334: .descriptors().iterator(); it.hasNext();) {
335: final GeneralParameterDescriptor descriptor = (GeneralParameterDescriptor) it
336: .next();
337: if (descriptor instanceof ParameterDescriptor) {
338: if (AbstractIdentifiedObject.nameMatches(descriptor,
339: name)) {
340: // TODO: remove the first cast with J2SE 1.5.
341: final ParameterValue value = (ParameterValue) ((ParameterDescriptor) descriptor)
342: .createValue();
343: values.add(value);
344: return value;
345: }
346: }
347: }
348: throw new ParameterNotFoundException(Errors.format(
349: ErrorKeys.MISSING_PARAMETER_$1, name), name);
350: }
351:
352: /**
353: * Returns all subgroups with the specified name. This method do not create new groups.
354: * If the requested group is optional (i.e.
355: * <code>{@linkplain ParameterDescriptor#getMinimumOccurs minimumOccurs} == 0</code>)
356: * and no value were set, then this method returns an empty set.
357: *
358: * @param name The case insensitive {@linkplain Identifier#getCode identifier code}
359: * of the parameter group to search for.
360: * @return The set of all parameter group for the given identifier code.
361: * @throws ParameterNotFoundException if no {@linkplain ParameterDescriptorGroup descriptor}
362: * was found for the given name.
363: */
364: public List/*<ParameterValueGroup>*/groups(String name)
365: throws ParameterNotFoundException {
366: ensureNonNull("name", name);
367: name = name.trim();
368: final List groups = new ArrayList(Math.min(values.size(), 10));
369: for (final Iterator it = values.iterator(); it.hasNext();) {
370: final GeneralParameterValue value = (GeneralParameterValue) it
371: .next();
372: if (value instanceof ParameterValueGroup) {
373: if (AbstractIdentifiedObject.nameMatches(value
374: .getDescriptor(), name)) {
375: groups.add(value);
376: }
377: }
378: }
379: /*
380: * No groups were found. Check if the group actually exists (i.e. is declared in the
381: * descriptor). If it doesn't exists, then an exception is thrown. If it exists (i.e.
382: * it is simply an optional group not yet defined), then returns an empty list.
383: */
384: if (groups.isEmpty()) {
385: final GeneralParameterDescriptor check = ((ParameterDescriptorGroup) descriptor)
386: .descriptor(name);
387: if (!(check instanceof ParameterDescriptorGroup)) {
388: throw new ParameterNotFoundException(Errors.format(
389: ErrorKeys.MISSING_PARAMETER_$1, name), name);
390: }
391: }
392: return groups;
393: }
394:
395: /**
396: * Creates a new group of the specified name. The specified name must be the
397: * {@linkplain Identifier#getCode identifier code} of a {@linkplain ParameterDescriptorGroup
398: * descriptor group}.
399: *
400: * @param name The case insensitive {@linkplain Identifier#getCode identifier code} of the
401: * parameter group to create.
402: * @return A newly created parameter group for the given identifier code.
403: * @throws ParameterNotFoundException if no {@linkplain ParameterDescriptorGroup descriptor}
404: * was found for the given name.
405: * @throws InvalidParameterCardinalityException if this parameter group already contains the
406: * {@linkplain ParameterDescriptorGroup#getMaximumOccurs maximum number of occurences}
407: * of subgroups of the given name.
408: */
409: public ParameterValueGroup addGroup(String name)
410: throws ParameterNotFoundException,
411: InvalidParameterCardinalityException {
412: final GeneralParameterDescriptor check = ((ParameterDescriptorGroup) descriptor)
413: .descriptor(name);
414: if (!(check instanceof ParameterDescriptorGroup)) {
415: throw new ParameterNotFoundException(Errors.format(
416: ErrorKeys.MISSING_PARAMETER_$1, name), name);
417: }
418: int count = 0;
419: for (final Iterator it = values.iterator(); it.hasNext();) {
420: final GeneralParameterValue value = (GeneralParameterValue) it
421: .next();
422: if (AbstractIdentifiedObject.nameMatches(value
423: .getDescriptor(), name)) {
424: count++;
425: }
426: }
427: if (count >= check.getMaximumOccurs()) {
428: throw new InvalidParameterCardinalityException(Errors
429: .format(ErrorKeys.TOO_MANY_OCCURENCES_$2, name,
430: new Integer(count)), name);
431: }
432: final ParameterValueGroup value = (ParameterValueGroup) // Remove this cast for J2SE 1.5
433: ((ParameterDescriptorGroup) check).createValue();
434: values.add(value);
435: return value;
436: }
437:
438: /**
439: * Compares the specified object with this parameter for equality.
440: *
441: * @param object The object to compare to {@code this}.
442: * @return {@code true} if both objects are equal.
443: */
444: public boolean equals(final Object object) {
445: if (object == this ) {
446: return true;
447: }
448: if (super .equals(object)) {
449: final ParameterGroup that = (ParameterGroup) object;
450: return Utilities.equals(this .values, that.values);
451: }
452: return false;
453: }
454:
455: /**
456: * Returns a hash value for this parameter.
457: *
458: * @return The hash code value. This value doesn't need to be the same
459: * in past or future versions of this class.
460: */
461: public int hashCode() {
462: return super .hashCode() ^ values.hashCode();
463: }
464:
465: /**
466: * Returns a deep copy of this group of parameter values.
467: * Included parameter values and subgroups are cloned recursively.
468: *
469: * @return A copy of this group of parameter values.
470: */
471: public Object clone() {
472: final ParameterGroup copy = (ParameterGroup) super .clone();
473: copy.values = (ArrayList/*<GeneralParameterValue>*/) copy.values
474: .clone();
475: for (int i = copy.values.size(); --i >= 0;) {
476: // TODO: remove cast with J2SE 1.5
477: copy.values.set(i, ((GeneralParameterValue) copy.values
478: .get(i)).clone());
479: }
480: copy.asList = null;
481: return copy;
482: }
483: }
|