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    }