001 /*
002 * GeoAPI - Java interfaces for OGC/ISO standards
003 * http://www.geoapi.org
004 *
005 * Copyright (C) 2003-2013 Open Geospatial Consortium, Inc.
006 * All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 * Permission to use, copy, and modify this software and its documentation, with
009 * or without modification, for any purpose and without fee or royalty is hereby
010 * granted, provided that you include the following on ALL copies of the software
011 * and documentation or portions thereof, including modifications, that you make:
012 *
013 * 1. The full text of this NOTICE in a location viewable to users of the
014 * redistributed or derivative work.
015 * 2. Notice of any changes or modifications to the OGC files, including the
016 * date changes were made.
017 *
018 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 * NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 * THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 * The name and trademarks of copyright holders may NOT be used in advertising or
028 * publicity pertaining to the software without specific, written prior permission.
029 * Title to copyright in this software and any associated documentation will at all
030 * times remain with copyright holders.
031 */
032 package org.opengis.util;
033
034 import java.io.Serializable;
035 import java.io.ObjectStreamException;
036 import java.io.InvalidObjectException;
037 import java.lang.reflect.Constructor;
038 import java.lang.reflect.Field;
039 import java.lang.reflect.Modifier;
040 import java.util.Collection;
041 import java.util.HashMap;
042 import java.util.Map;
043
044 import org.opengis.annotation.UML;
045
046 import static org.opengis.annotation.Specification.*;
047
048
049 /**
050 * Base class for all code lists. Subclasses shall provides a {@code values()} method
051 * which returns all {@code CodeList} element in an array of the appropriate class.
052 *
053 * <p>Code lists are extensible, i.e. invoking the {@code valueOf(String)} method in any subclass
054 * will automatically add the newly created {@code CodeList} element in the array to be returned
055 * by {@code values()}.</p>
056 *
057 * @param <E> The type of this code list.
058 *
059 * @author Martin Desruisseaux (IRD)
060 * @version 3.0
061 * @since 1.0
062 */
063 @UML(identifier="CodeList", specification=ISO_19103)
064 public abstract class CodeList<E extends CodeList<E>> implements Comparable<E>, Serializable {
065 /**
066 * Serial number for compatibility with different versions.
067 */
068 private static final long serialVersionUID = 5655809691319522885L;
069
070 /**
071 * The values for each code list.
072 */
073 @SuppressWarnings("rawtypes")
074 private static final Map<Class<? extends CodeList>, Collection<? extends CodeList>> VALUES =
075 new HashMap<Class<? extends CodeList>, Collection<? extends CodeList>>();
076
077 /**
078 * The types expected in constructors.
079 */
080 @SuppressWarnings({"unchecked","rawtypes"})
081 private static final Class<String>[] CONSTRUCTOR_PARAMETERS = new Class[] {
082 String.class
083 };
084
085 /**
086 * The code value.
087 */
088 private transient final int ordinal;
089
090 /**
091 * The code name.
092 */
093 private final String name;
094
095 /**
096 * The identifier declared in the {@link UML} annotation, or an empty string if there is
097 * no such annotation or if the annotation contains an empty string. This field will be
098 * computed only when first needed.
099 */
100 private transient String identifier;
101
102 /**
103 * Creates a new code list element and add it to the given collection. Subclasses
104 * will typically give a static reference to an {@link java.util.ArrayList} for
105 * the {@code values} argument. This list is used for {@code values()}
106 * method implementations.
107 *
108 * @param name The code name.
109 * @param values The collection to add the element to.
110 */
111 @SuppressWarnings({"unchecked","rawtypes"})
112 protected CodeList(String name, final Collection<E> values) {
113 this.name = (name = name.trim());
114 synchronized (values) {
115 this.ordinal = values.size();
116 if (!values.add((E) this)) {
117 throw new IllegalArgumentException("Duplicated value: " + name);
118 }
119 }
120 final Class<? extends CodeList> codeType = getClass();
121 synchronized (VALUES) {
122 final Collection<? extends CodeList> previous = VALUES.put(codeType, values);
123 if (previous != null && previous != values) {
124 VALUES.put(codeType, previous); // Roll back
125 throw new IllegalArgumentException("List already exists: " + values);
126 }
127 }
128 }
129
130 /**
131 * Used by {@link CodeList#valueOf(Class, Filter)} to select codes matching an arbitrary
132 * criterion.
133 *
134 * @departure extension
135 * The inner <code>CodeList.Filter</code> interface is not part of the OGC specification.
136 * It has been added because <code>CodeList</code> is one of the few concrete classes in
137 * GeoAPI and there is a need to give some user control over the behavior of the
138 * <code>CodeList</code> implementation.
139 *
140 * @since 2.3
141 */
142 public static interface Filter {
143 /**
144 * Returns {@code true} if the given code matches the criterion defined by this filter.
145 *
146 * @param code The code to test.
147 * @return {@code true} if the code matches the criterion.
148 */
149 boolean accept(CodeList<?> code);
150
151 /**
152 * Returns the name of the code being looked for, or {@code null} if unknown.
153 * This method is invoked by {@link CodeList#valueOf(Class, Filter)} if no code
154 * match the criterion defined by this filter. In such case, there is a choice:
155 *
156 * <ul>
157 * <li>If this method returns a non-null name, then a new code of that name is created.</li>
158 * <li>Otherwise, no new code is created and {@code CodeList.valueOf} returns {@code null}.</li>
159 * </ul>
160 *
161 * @return The name of the code being looked for, or {@code null}.
162 */
163 String codename();
164 }
165
166 /**
167 * Returns the code of the given type that matches the given name, or returns a new one if none
168 * match it. More specifically, this methods returns the first instance of the given class for
169 * which <code>{@linkplain #name()}.{@linkplain String#equals equals}(name)</code> is {@code true}.
170 * If no such instance is found, then a new instance is created using the constructor expecting a
171 * single {@link String} argument.
172 *
173 * <p><b>Implementation note:</b> The {@code codeType} class needs to be initialized before to
174 * invoke this method. This is usually the case when the caller is a static method of the
175 * {@code codeType} class. However in other situations, callers may need to initialize
176 * explicitely the given class.</p>
177 *
178 * @param <T> The compile-time type given as the {@code codeType} parameter.
179 * @param codeType The type of code list.
180 * @param name The name of the code to obtain, or {@code null}.
181 * @return A code matching the given name, or {@code null} if the name is null.
182 *
183 * @departure integration
184 * Provided by analogy with the methods in the JSE 5 <code>Enum</code> class.
185 */
186 public static <T extends CodeList<T>> T valueOf(final Class<T> codeType, String name) {
187 if (name == null) {
188 return null;
189 }
190 name = name.trim();
191 final String n = name;
192 return valueOf(codeType, new Filter() {
193 @Override
194 public boolean accept(CodeList<?> code) {
195 return code.name().equals(n);
196 }
197
198 @Override
199 public String codename() {
200 return n;
201 }
202 });
203 }
204
205 /**
206 * Returns the code of the given type that matches the given criterion, or returns a new one
207 * if none match it. More specifically, this methods returns the first element (in declaration
208 * order) of the given class where <code>filter.{@linkplain Filter#accept accept}(code)</code>
209 * returns {@code true}. If no such element is found, then there is a choice:
210 *
211 * <ul>
212 * <li>If {@link Filter#codename()} returns {@code null}, then this method returns {@code null}.</li>
213 * <li>Otherwise a new instance is created using the constructor expecting a single {@link String}
214 * argument, which is given the value returned by {@code codename()}.</li>
215 * </ul>
216 *
217 * @param <T> The compile-time type given as the {@code codeType} parameter.
218 * @param codeType The type of code list.
219 * @param filter The criterion for the code to obtain.
220 * @return A code matching the given criterion, or {@code null} if their is no match and
221 * {@link Filter#codename()} returns {@code null}.
222 *
223 * @since 2.3
224 */
225 public static <T extends CodeList<T>> T valueOf(final Class<T> codeType, final Filter filter) {
226 @SuppressWarnings("rawtypes")
227 final Collection<? extends CodeList> values;
228 synchronized (VALUES) {
229 values = VALUES.get(codeType);
230 if (values == null) {
231 if (codeType == null) {
232 throw new IllegalArgumentException("Code type is null");
233 } else {
234 throw new IllegalStateException("No collection of " + codeType.getSimpleName());
235 }
236 }
237 }
238 synchronized (values) {
239 for (final CodeList<?> code : values) {
240 if (filter.accept(code)) {
241 return codeType.cast(code);
242 }
243 }
244 final String name = filter.codename();
245 if (name == null) {
246 return null;
247 }
248 try {
249 final Constructor<T> constructor = codeType.getDeclaredConstructor(CONSTRUCTOR_PARAMETERS);
250 constructor.setAccessible(true);
251 return constructor.newInstance(name);
252 } catch (Exception exception) {
253 throw new IllegalArgumentException("Can't create code of type " + codeType.getSimpleName(), exception);
254 }
255 }
256 }
257
258 /**
259 * Returns the list of codes of the same kind than this code.
260 * This is similar to the static {@code values()} method provided in {@code CodeList}
261 * subclasses, except that {@code family()} does not require the class to be known at
262 * compile-time - provided that at leat one instance of the family is available. The
263 * static {@code values()} method has the opposite constraints (does not require a code
264 * instance, but the class needs to be known at compile time unless
265 * {@linkplain java.lang.reflect reflection} is used).
266 *
267 * @return The codes of the same kind than this code.
268 *
269 * @departure integration
270 * Provided by analogy with <code>Enum.family()</code>, which was defined in a initial
271 * draft of JSE 5 before the final release.
272 */
273 public abstract E[] family();
274
275 /**
276 * Returns all the names of this code. The returned array contains the
277 * following elements, with duplicated values and null values removed:
278 *
279 * <ul>
280 * <li>The programmatic {@linkplain #name() name}</li>
281 * <li>The UML {@linkplain #identifier() identifier}</li>
282 * <li>The {@linkplain org.opengis.metadata.identification.CharacterSet#toCharset() charset} name
283 * ({@code CharacterSet} code list only)</li>
284 * </ul>
285 *
286 * Those names are typically equal except for the case (programmatic names are upper case
287 * while UML names are lower case) and special characters like {@code '-'}.
288 *
289 * @return All names of this code constant. This array is never null and never empty.
290 *
291 * @departure extension
292 * Defined because each <code>CodeList</code> has at least two names, the Java programmatic
293 * name and the UML identifier, while some subclasses have additional names.
294 *
295 * @since 2.3
296 */
297 public String[] names() {
298 final String name = this.name;
299 final String identifier = identifier();
300 if (identifier != null && !identifier.equals(name)) {
301 return new String[] {name, identifier};
302 } else {
303 return new String[] {name};
304 }
305 }
306
307 /**
308 * Returns the programmatic name of this code list constant. This
309 * is the name of the public static field which declare the code.
310 *
311 * @departure integration
312 * Provided by analogy with the methods in the JSE 5 <code>Enum</code> class.
313 *
314 * @return The name of this code constant.
315 */
316 public final String name() {
317 return name;
318 }
319
320 /**
321 * Returns the identifier declared in the {@link UML} annotation, or {@code null} if none.
322 * The UML identifier shall be the ISO or OGC name for this code constant.
323 *
324 * @return The ISO/OGC identifier for this code constant, or {@code null} if none.
325 *
326 * @departure extension
327 * Defined because each <code>CodeList</code> has a UML identifier in addition of the Java
328 * programmatic name.
329 *
330 * @since 2.2
331 */
332 public String identifier() {
333 // Save the field in a local variable for protection against concurrent change (this
334 // operation is garanteed atomic according Java specification). We don't synchronize
335 // since it is not a problem if this method is executed twice in concurrent threads.
336 String identifier = this.identifier;
337 if (identifier == null) {
338 @SuppressWarnings("rawtypes")
339 final Class<? extends CodeList> codeType = getClass();
340 Field field;
341 try {
342 field = codeType.getField(name);
343 } catch (NoSuchFieldException e) {
344 // There is no field for a code of this name. It may be normal, since the user
345 // may have created a custom CodeList without declaring it as a constant.
346 field = null;
347 }
348 if (field != null && Modifier.isStatic(field.getModifiers())) {
349 final Object value;
350 try {
351 value = field.get(null);
352 } catch (IllegalAccessException e) {
353 // Should never happen since getField(String) returns only public fields.
354 throw new AssertionError(e);
355 }
356 if (equals(value)) {
357 final UML annotation = field.getAnnotation(UML.class);
358 if (annotation != null) {
359 identifier = annotation.identifier();
360 }
361 }
362 }
363 if (identifier == null) {
364 identifier = "";
365 }
366 this.identifier = identifier;
367 }
368 return identifier.length() != 0 ? identifier : null;
369 }
370
371 /**
372 * Returns the ordinal of this code constant. This is its position in its elements declaration,
373 * where the initial constant is assigned an ordinal of zero.
374 *
375 * @return The position of this code constants in elements declaration.
376 *
377 * @departure integration
378 * Provided by analogy with the methods in the JSE 5 <code>Enum</code> class.
379 */
380 public final int ordinal() {
381 return ordinal;
382 }
383
384 /**
385 * Compares this code with the specified object for order. Returns a
386 * negative integer, zero, or a positive integer as this object is less
387 * than, equal to, or greater than the specified object.
388 *
389 * <p>Code list constants are only comparable to other code list constants of the
390 * same type. The natural order implemented by this method is the order in which
391 * the constants are declared.</p>
392 *
393 * @param other The code constant to compare with this code.
394 * @return A negative value if the given code is less than this code,
395 * a positive value if greater or 0 if equal.
396 */
397 @Override
398 @SuppressWarnings("rawtypes")
399 public final int compareTo(final E other) {
400 final Class<? extends CodeList> ct = this.getClass();
401 final Class<? extends CodeList> co = other.getClass();
402 if (!ct.equals(co)) {
403 throw new ClassCastException("Can't compare " + ct.getSimpleName() + " to " + co.getSimpleName());
404 }
405 return ordinal - ((CodeList<?>) other).ordinal;
406 }
407
408 /**
409 * Compares the specified object with this code list for equality. This method compares only
410 * {@linkplain #ordinal() ordinal} values for consistency with the {@link #compareTo(CodeList)} method.
411 * Ordinal values are unique for each code list element of the same class.
412 *
413 * @param object The object to compare with this code.
414 * @return {@code true} if the given object is equals to this code.
415 *
416 * @since 2.2
417 */
418 @Override
419 public final boolean equals(final Object object) {
420 if (object != null && object.getClass().equals(getClass())) {
421 return ordinal == ((CodeList<?>) object).ordinal;
422 }
423 return false;
424 }
425
426 /**
427 * Returns a string representation of this code list.
428 */
429 @Override
430 public String toString() {
431 return getClass().getSimpleName() + '[' + name + ']';
432 }
433
434 /**
435 * Resolves the code list to an unique instance after deserialization. The instance is resolved
436 * using its {@linkplain #name() name} only (not its {@linkplain #ordinal() ordinal}).
437 *
438 * @return This code list as an unique instance.
439 * @throws ObjectStreamException if the deserialization failed.
440 */
441 @SuppressWarnings("rawtypes")
442 protected Object readResolve() throws ObjectStreamException {
443 final Class<? extends CodeList> codeType = getClass();
444 final Collection<? extends CodeList> values;
445 synchronized (VALUES) {
446 values = VALUES.get(codeType);
447 }
448 if (values != null) {
449 synchronized (values) {
450 for (final CodeList<?> code : values) {
451 if (!codeType.isInstance(code)) {
452 // Paranoiac check - should never happen unless the subclass
453 // modifies itself the collection given to the constructor,
454 // in which case we will not touch it.
455 return this;
456 }
457 if (code.name.equals(name)) {
458 return code;
459 }
460 }
461 // We have verified with codeType.isInstance(code) that every elements are
462 // of the appropriate class. This is the best we can do for type safety.
463 @SuppressWarnings("unchecked")
464 final Collection<CodeList> unsafe = (Collection) values;
465 if (!unsafe.add(this)) {
466 // Paranoiac check - should never happen.
467 throw new InvalidObjectException(name);
468 }
469 }
470 }
471 return this;
472 }
473 }