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.io.Writer;
024: import java.io.IOException;
025: import java.io.Serializable;
026: import java.lang.reflect.Array;
027: import java.util.Iterator;
028:
029: // OpenGIS dependencies
030: import org.opengis.parameter.ParameterValue;
031: import org.opengis.parameter.ParameterValueGroup;
032: import org.opengis.parameter.GeneralParameterValue;
033: import org.opengis.parameter.GeneralParameterDescriptor;
034:
035: // Geotools dependencies
036: import org.geotools.io.TableWriter;
037: import org.geotools.referencing.wkt.Formattable;
038: import org.geotools.referencing.wkt.Formatter;
039: import org.geotools.resources.Utilities;
040: import org.geotools.resources.i18n.Errors;
041: import org.geotools.resources.i18n.ErrorKeys;
042:
043: /**
044: * Abstract parameter value or group of parameter values.
045: *
046: * @since 2.1
047: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/parameter/AbstractParameter.java $
048: * @version $Id: AbstractParameter.java 23632 2006-12-29 22:13:51Z desruisseaux $
049: * @author Martin Desruisseaux
050: *
051: * @see AbstractParameterDescriptor
052: */
053: public abstract class AbstractParameter extends Formattable implements
054: GeneralParameterValue, Serializable {
055: /**
056: * Serial number for interoperability with different versions.
057: */
058: private static final long serialVersionUID = 8458179223988766398L;
059:
060: /**
061: * The abstract definition of this parameter or group of parameters.
062: */
063: final GeneralParameterDescriptor descriptor;
064:
065: /**
066: * Constructs a parameter value from the specified descriptor.
067: *
068: * @param descriptor The abstract definition of this parameter or group of parameters.
069: */
070: protected AbstractParameter(
071: final GeneralParameterDescriptor descriptor) {
072: this .descriptor = descriptor;
073: ensureNonNull("descriptor", descriptor);
074: }
075:
076: /**
077: * Returns the abstract definition of this parameter or group of parameters.
078: */
079: public GeneralParameterDescriptor getDescriptor() {
080: return descriptor;
081: }
082:
083: /**
084: * Makes sure that an argument is non-null. This method was already defined in
085: * {@link org.geotools.referencing.AbstractIdentifiedObject}, but is defined here again
086: * in order to get a more appropriate stack trace, and for access by class which do not
087: * inherit from {@link org.geotools.referencing.AbstractIdentifiedObject}.
088: *
089: * @param name Argument name.
090: * @param object User argument.
091: * @throws IllegalArgumentException if {@code object} is null.
092: */
093: static void ensureNonNull(final String name, final Object object)
094: throws IllegalArgumentException {
095: if (object == null) {
096: throw new IllegalArgumentException(Errors.format(
097: ErrorKeys.NULL_ARGUMENT_$1, name));
098: }
099: }
100:
101: /**
102: * Makes sure an array element is non-null. This is
103: * a convenience method for subclass constructors.
104: *
105: * @param name Argument name.
106: * @param array The array to look at.
107: * @param index Index of the element to check.
108: * @throws IllegalArgumentException if {@code array[i]} is null.
109: */
110: static void ensureNonNull(final String name, final Object[] array,
111: final int index) throws IllegalArgumentException {
112: if (array[index] == null) {
113: throw new IllegalArgumentException(Errors.format(
114: ErrorKeys.NULL_ARGUMENT_$1, name + '[' + index
115: + ']'));
116: }
117: }
118:
119: /**
120: * Verify that the specified value is of the specified class.
121: *
122: * @param valueClass the expected class.
123: * @param value The expected value, or {@code null}.
124: * @throws IllegalArgumentException if {@code value} is non-null and has a non-assignable
125: * class.
126: */
127: static void ensureValidClass(final Class valueClass,
128: final Object value) throws IllegalArgumentException {
129: if (value != null) {
130: if (!valueClass.isAssignableFrom(value.getClass())) {
131: throw new IllegalArgumentException(Errors.format(
132: ErrorKeys.ILLEGAL_CLASS_$2, Utilities
133: .getShortClassName(value), Utilities
134: .getShortName(valueClass)));
135: }
136: }
137: }
138:
139: /**
140: * Returns an exception initialized with a "Unitless parameter" error message for the
141: * specified descriptor.
142: */
143: static IllegalStateException unitlessParameter(
144: final GeneralParameterDescriptor descriptor) {
145: return new IllegalStateException(Errors.format(
146: ErrorKeys.UNITLESS_PARAMETER_$1, getName(descriptor)));
147: }
148:
149: /**
150: * Convenience method returning the name of the specified descriptor. This method is used
151: * mostly for output to be read by human, not for processing. Consequently, we may consider
152: * to returns a localized name in a future version.
153: */
154: static String getName(final GeneralParameterDescriptor descriptor) {
155: return descriptor.getName().getCode();
156: }
157:
158: /**
159: * Returns a copy of this parameter value or group.
160: */
161: public Object clone() {
162: try {
163: return super .clone();
164: } catch (CloneNotSupportedException exception) {
165: // Should not happen, since we are cloneable
166: throw new AssertionError(exception);
167: }
168: }
169:
170: /**
171: * Compares the specified object with this parameter for equality.
172: *
173: * @param object The object to compare to {@code this}.
174: * @return {@code true} if both objects are equal.
175: */
176: public boolean equals(final Object object) {
177: if (object != null && object.getClass().equals(getClass())) {
178: final AbstractParameter that = (AbstractParameter) object;
179: return Utilities.equals(this .descriptor, that.descriptor);
180: }
181: return false;
182: }
183:
184: /**
185: * Returns a hash value for this parameter. This value doesn't need
186: * to be the same in past or future versions of this class.
187: */
188: public int hashCode() {
189: return descriptor.hashCode() ^ (int) serialVersionUID;
190: }
191:
192: /**
193: * Returns a string representation of this parameter. The default implementation
194: * delegates the work to {@link #write}, which should be overridden by subclasses.
195: */
196: public final String toString() {
197: final TableWriter table = new TableWriter(null, 1);
198: table.setMultiLinesCells(true);
199: try {
200: write(table);
201: } catch (IOException exception) {
202: // Should never happen, since we write to a StringWriter.
203: throw new AssertionError(exception);
204: }
205: return table.toString();
206: }
207:
208: /**
209: * Write the content of this parameter to the specified table. This method make it easier
210: * to align values properly than overriding the {@link #toString} method. The table's columns
211: * are defined as below:
212: * <ol>
213: * <li>The parameter name</li>
214: * <li>The separator</li>
215: * <li>The parameter value</li>
216: * </ol>
217: *
218: * <P>The default implementation is suitable for most cases. However, subclasses are free to
219: * override this method with the following idiom:</P>
220: *
221: * <blockquote><pre>
222: * table.{@linkplain TableWriter#write(String) write}("<var>parameter name</var>");
223: * table.{@linkplain TableWriter#nextColumn() nextColumn}()
224: * table.{@linkplain TableWriter#write(String) write}('=');
225: * table.{@linkplain TableWriter#nextColumn() nextColumn}()
226: * table.{@linkplain TableWriter#write(String) write}("<var>parameter value</var>");
227: * table.{@linkplain TableWriter#nextLine() nextLine}()
228: * </pre></blockquote>
229: *
230: * @param table The table where to format the parameter value.
231: * @throws IOException if an error occurs during output operation.
232: */
233: protected void write(final TableWriter table) throws IOException {
234: table.write(getName(descriptor));
235: table.nextColumn();
236: if (this instanceof ParameterValue) {
237: /*
238: * Provides a default implementation for parameter value. This implementation doesn't
239: * need to be a Geotools's one. Putting a default implementation here avoid duplication
240: * in all subclasses implementing the same interface.
241: */
242: table.write('=');
243: table.nextColumn();
244: append(table, ((ParameterValue) this ).getValue());
245: } else if (this instanceof ParameterValueGroup) {
246: /*
247: * Provides a default implementation for parameter value group, for the same reasons
248: * then the previous block. Reminder: the above 'instanceof' check for interface, not
249: * for subclass. This explain why we use it instead of method overriding.
250: */
251: table.write(':');
252: table.nextColumn();
253: TableWriter inner = null;
254: for (final Iterator it = ((ParameterValueGroup) this )
255: .values().iterator(); it.hasNext();) {
256: final GeneralParameterValue value = (GeneralParameterValue) it
257: .next();
258: if (value instanceof AbstractParameter) {
259: if (inner == null) {
260: inner = new TableWriter(table, 1);
261: }
262: ((AbstractParameter) value).write(inner);
263: } else {
264: // Unknow implementation. It will break the formatting. Too bad...
265: if (inner != null) {
266: inner.flush();
267: inner = null;
268: }
269: table.write(value.toString());
270: table.write(System.getProperty("line.separator",
271: "\r"));
272: }
273: }
274: if (inner != null) {
275: inner.flush();
276: }
277: } else {
278: /*
279: * No know parameter value for this default implementation.
280: */
281: }
282: table.nextLine();
283: }
284:
285: /**
286: * Append the specified value to a stream. If the value is an array, then
287: * the array element are appended recursively (i.e. the array may contains
288: * sub-array).
289: */
290: private static void append(final Writer buffer, final Object value)
291: throws IOException {
292: if (value == null) {
293: buffer.write("null");
294: } else if (value.getClass().isArray()) {
295: buffer.write('{');
296: final int length = Array.getLength(value);
297: final int limit = Math.min(5, length);
298: for (int i = 0; i < limit; i++) {
299: if (i != 0) {
300: buffer.write(", ");
301: }
302: append(buffer, Array.get(value, i));
303: }
304: if (length > limit) {
305: buffer.write(", ...");
306: }
307: buffer.write('}');
308: } else {
309: final boolean isNumeric = (value instanceof Number);
310: if (!isNumeric) {
311: buffer.write('"');
312: }
313: buffer.write(value.toString());
314: if (!isNumeric) {
315: buffer.write('"');
316: }
317: }
318: }
319:
320: /**
321: * Format the inner part of this parameter as
322: * <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
323: * Known Text</cite> (WKT)</A>. This method doesn't need to be overridden, since the formatter
324: * already know how to {@linkplain Formatter#append(GeneralParameterValue) format parameters}.
325: */
326: protected final String formatWKT(final Formatter formatter) {
327: return "PARAMETER";
328: }
329: }
|