001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2003-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.util; 019 020import java.io.Serializable; 021import java.io.ObjectStreamException; 022import java.lang.reflect.Array; 023import java.lang.reflect.Constructor; 024import java.lang.reflect.Field; 025import java.lang.reflect.Modifier; 026import java.lang.reflect.InaccessibleObjectException; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Map; 030import java.util.WeakHashMap; 031import java.util.Objects; 032import java.util.Optional; 033import java.util.function.Function; 034import java.util.function.Predicate; 035 036import org.opengis.annotation.UML; 037import static org.opengis.annotation.Specification.*; 038import org.opengis.geoapi.internal.Vocabulary; 039import org.opengis.geoapi.internal.Errors; 040 041 042/** 043 * Base class for all code lists. Code lists are like enumerations, but extensible. 044 * For example, invoking the {@code valueOf(String)} method in any subclass with a 045 * name that was not previously defined will create a new {@code CodeList} element 046 * and automatically add it to the arrays returned by {@code values()}. 047 * 048 * <h2>Guidance for subclasses</h2> 049 * The parameter type {@code <E>} <em>shall</em> be the same type as the code list subclass. 050 * In other words, if the subclass is {@code Foo}, then it must extend {@code CodeList<Foo>}. 051 * In addition, subclasses <em>should</em> provide {@code values(String)} and {@code values()} methods. 052 * The following code snippet is a suggested pattern for subclasses: 053 * 054 * {@snippet lang="java" : 055 * public final class Foo extends CodeList<Foo> { 056 * public static final Foo BAR = new Foo("BAR"); 057 * public static final Foo BIZ = new Foo("BIZ"); 058 * 059 * private Foo(String name) { 060 * super(name); 061 * } 062 * 063 * public static Foo valueOf(String name) { 064 * return valueOf(Foo.class, name, Foo::new).get(); 065 * } 066 * 067 * public static Foo[] values() { 068 * return values(Foo.class); 069 * } 070 * 071 * @Override 072 * public Foo[] family() { 073 * return values(); 074 * } 075 * } 076 * } 077 * 078 * Above snippet is sufficient for classes in exported packages. If a code list is defined in a non-exported package, 079 * then its static fields may need to be initialized with {@code valueOf(String)} instead of {@code new Foo(String)}. 080 * It will have a small performance cost, but is needed because of Java Module System restrictions on reflection. 081 * 082 * @param <E> the type of this code list. 083 * 084 * @author Martin Desruisseaux (IRD, Geomatys) 085 * @version 3.1 086 * @since 1.0 087 */ 088@UML(identifier="CodeList", specification=ISO_19103) 089public abstract class CodeList<E extends CodeList<E>> implements ControlledVocabulary, Comparable<E>, Serializable { 090 /** 091 * Serial number for compatibility with different versions. 092 */ 093 private static final long serialVersionUID = 5655809691319522885L; 094 095 /** 096 * The values for each code list. 097 * Map keys are the {@code CodeList} classes for which elements are listed, and map values are those elements. 098 * Each code list element shall be stored in the list at the index corresponding to its {@link #ordinal} value. 099 * Elements are stored as {@code CodeList} instances if available, or by their {@code String} names otherwise. 100 * Names are sometime used instead of instances because assigning {@code this} at construction time is unsafe. 101 * Those names will be replaced by the actual instances by the {@code valueOf(String)} method or by reflection. 102 * 103 * <h4>Class unloading</h4> 104 * The use of {@link WeakHashMap} allows class unloading, but can work only as long as the list 105 * associated to a class contains only {@code String} elements. After storing at least one instance, 106 * the indirect references to the class prevent the {@code Class} key from being garbage-collected. 107 * 108 * @see #values(Class) 109 */ 110 private static final Map<Class<? extends CodeList<?>>, Elements> VALUES = new WeakHashMap<>(); 111 112 /** 113 * Elements stored in the {@link #VALUES} map for a given {@code CodeList} class. 114 * This list contains {@link CodeList} instances when they are fully constructed, 115 * or only their names when storing the instance would be unsafe ("this-escape"). 116 * The {@link #get(int)} method may return a {@link String} or a {@link CodeList}. 117 * If a {@code CodeList} is desired, use {@link #resolve(Class, int)} instead. 118 */ 119 @SuppressWarnings("serial") // Not intended to be serialized. 120 private static final class Elements extends ArrayList<Object> { 121 /** 122 * Whether all code list names in this list have been replaced by the actual instances. 123 * This is used for faster {@link #toArray(Class)} execution when there is no longer a 124 * need to check the type of each element. 125 * 126 * @see #toArray(Class) 127 */ 128 boolean resolved; 129 130 /** 131 * Creates a new, initially empty, list. 132 * 133 * @param type the {@link #VALUES} map key to which this list will be associated. 134 */ 135 Elements(final Class<? extends CodeList<?>> type) { 136 super(capacity(type)); 137 } 138 139 /** Workaround while waiting for JEP 447: Statements before super(…). */ 140 private static int capacity(final Class<? extends CodeList<?>> type) { 141 var desc = type.getAnnotation(Vocabulary.class); 142 return (desc != null) ? desc.capacity() : 8; // Code lists typically have few elements. 143 } 144 145 /** 146 * Returns the element at the given index as a code list element of the specified class. 147 * Callers must invoke this method in a block synchronized on {@code this} list. 148 * 149 * @param <E> the compile-time type given as the {@code codeType} parameter. 150 * @param codeType the type of the code list for which to get an element. 151 * @param index index of the element to get. 152 * @return element at the specified index. 153 * @throws InaccessibleObjectException if the element needs to be resolved by reflection and this operation failed. 154 * A failure is generally caused by an error in the declaration of static fields in the code list. 155 * @throws IllegalStateException if the code list is inconsistent (e.g., element having wrong index). 156 */ 157 final <E extends CodeList<E>> E resolve(final Class<E> codeType, final int index) { 158 final E element; 159 final Object code = get(index); 160 if (code instanceof String) try { 161 final Field field; 162 if (codeType.isAnnotationPresent(Vocabulary.class)) { // True if the code list is a GeoAPI class. 163 field = codeType.getDeclaredField((String) code); 164 field.setAccessible(true); 165 } else { 166 field = codeType.getField((String) code); // Be more conservative for user classes. 167 } 168 element = codeType.cast(field.get(null)); 169 if (element.ordinal() == index && element.name().equals(code)) { 170 set(index, element); 171 } else { 172 throw new IllegalStateException("Conflict between " + element + " and " + code + '.'); 173 } 174 } catch (ReflectiveOperationException | NullPointerException | ClassCastException e) { 175 /* 176 * The search by reflection may fail with: 177 * - IllegalAccessException if the package containing `codeType` is not exported, 178 * - NoSuchFieldException if the field does not exist, 179 * - NullPointerException if the field is not static, or 180 * - ClassCastException if the field value is not an instance of <E>. 181 */ 182 throw (InaccessibleObjectException) new InaccessibleObjectException(cannotRead(codeType, code)).initCause(e); 183 } else { 184 element = codeType.cast(code); 185 } 186 return element; 187 } 188 189 /** 190 * Produces the message for a field that cannot be read by reflection. 191 * Used for exception messages or for log record messages. 192 * 193 * @param codeType the type of the code list. 194 * @param code the code that cannot be read. 195 * @return the message to use in exception or in log record. 196 */ 197 static String cannotRead(final Class<?> codeType, final Object code) { 198 return "Cannot read the " + codeType.getSimpleName() + '.' + code + " field value."; 199 } 200 201 /** 202 * Returns all elements in this list. 203 * 204 * @param <E> the compile-time type given as the {@code codeType} parameter. 205 * @param codeType the type of the code list for which to get all elements. 206 * @return all elements that are known at the time that this method is invoked. 207 * @throws InaccessibleObjectException if an element needs to be resolved by reflection and this operation failed. 208 * A failure is generally caused by an error in the declaration of static fields in the code list. 209 */ 210 final synchronized <E extends CodeList<E>> E[] toArray(final Class<E> codeType) { 211 @SuppressWarnings("unchecked") 212 final E[] array = (E[]) Array.newInstance(codeType, size()); 213 if (resolved) { 214 return toArray(array); 215 } 216 for (int i=0; i<array.length; i++) { 217 array[i] = resolve(codeType, i); 218 } 219 resolved = true; 220 return array; 221 } 222 } 223 224 /** 225 * The code value as a sequential number incremented in the order in which codes are created. 226 * This value is set at construction time and should be considered final. It is potentially 227 * modified only during deserialization, because the Java serialization mechanism bypasses 228 * the constructor. 229 * 230 * @see #ordinal() 231 * @see #readResolve() 232 */ 233 private transient int ordinal; 234 235 /** 236 * The code name. If this {@code CodeList} instance is stored in a static field 237 * of the code list class, then this name shall be identical to the field name. 238 * 239 * @see #name() 240 * @see #names() 241 */ 242 private final String name; 243 244 /** 245 * The identifier declared in the {@link UML} annotation, or an empty string if there is 246 * no such annotation or if the annotation contains an empty string. This field will be 247 * computed only when first needed. 248 * 249 * @see #identifier() 250 * @see #names() 251 */ 252 private transient String identifier; 253 254 /** 255 * Creates a new code list element and add it to the given collection. Subclasses 256 * will typically give a static reference to an {@link java.util.ArrayList} for 257 * the {@code values} argument. This list is used for {@code values()} 258 * method implementations. 259 * 260 * @param name the code name. Shall be the name of the static field if such field exist. 261 * @param values the collection to add the element to. Shall be the same for all elements. 262 * 263 * @deprecated This constructor is unsafe because a reference to {@code this} escapes before subclass is 264 * fully initialized (see <a href="https://github.com/opengeospatial/geoapi/issues/91">issue #91</a>). 265 * Use {@link #CodeList(String)} instead. 266 */ 267 @Deprecated(since="3.1", forRemoval=true) 268 protected CodeList(String name, final Collection<E> values) { 269 this(name); 270 synchronized (values) { 271 this.ordinal = values.size(); 272 if (!values.add((E) this)) { 273 // Should not happen with standard collection implementations. 274 throw new IllegalArgumentException("Duplicated value: " + name); 275 } 276 } 277 } 278 279 /** 280 * Creates a new code list element. If this constructor is invoked for initializing a static field, 281 * then the given name <em>shall</em> be the case-sensitive name of the static field. 282 * 283 * @param name the code name. Shall be the name of the static field if such field exist. 284 * 285 * @since 3.1 286 */ 287 protected CodeList(final String name) { 288 this.name = name; 289 Class<?> type = getClass(); 290 while (type.isAnonymousClass()) { 291 type = type.getSuperclass(); 292 if (type.equals(CodeList.class)) { 293 throw new IllegalStateException("Class shall not be anonymous."); 294 } 295 } 296 @SuppressWarnings("unchecked") // The following cast should never fail. 297 var codeType = (Class<? extends CodeList<?>>) type; 298 final Elements values = getOrCreateList(codeType); 299 synchronized (values) { 300 ordinal = values.size(); 301 values.add(name); // Needed for incrementing the list size. 302 values.resolved = false; 303 } 304 } 305 306 /** 307 * Returns the list associated to the specified type of code list, creating the list if needed. 308 * This method should be invoked only when the {@code codeType} class is known to be initialized. 309 * If the class may be uninitialized, use {@link #valueList(Class)} instead. 310 * 311 * @param codeType the type of code list for which to get the code list values. 312 * @return all values for the given type. Never {@code null} but potentially empty. 313 */ 314 private static Elements getOrCreateList(final Class<? extends CodeList<?>> codeType) { 315 synchronized (VALUES) { 316 return VALUES.computeIfAbsent(codeType, Elements::new); 317 } 318 } 319 320 /** 321 * Used by {@link CodeList#valueOf(Class, Filter)} to select codes matching an arbitrary 322 * criterion. 323 * 324 * @deprecated Replaced by {@link Predicate} from the standard Java library. 325 */ 326 @Deprecated(since="3.1", forRemoval=true) 327 public static interface Filter { 328 /** 329 * Returns {@code true} if the given code matches the criterion defined by this filter. 330 * 331 * @param code the code to test. 332 * @return {@code true} if the code matches the criterion. 333 */ 334 boolean accept(CodeList<?> code); 335 336 /** 337 * Returns the name of the code being looked for, or {@code null} if unknown. 338 * This method is invoked by {@link CodeList#valueOf(Class, Filter)} if no code 339 * match the criterion defined by this filter. In such case, there is a choice: 340 * 341 * <ul> 342 * <li>If this method returns a non-null name, then a new code of that name is created.</li> 343 * <li>Otherwise, no new code is created and {@code CodeList.valueOf} returns {@code null}.</li> 344 * </ul> 345 * 346 * @return the name of the code being looked for, or {@code null}. 347 */ 348 String codename(); 349 } 350 351 /** 352 * Returns the code of the given type that matches the given name, or creates a new code if there is no match. 353 * More specifically, this methods returns the first instance of the given class for which 354 * <code>{@linkplain #name()}.{@linkplain String#equals equals}(name)</code> is {@code true}. 355 * If no such instance is found, then a new instance is created using the constructor expecting 356 * a single {@link String} argument. 357 * 358 * <p><strong>Note that invoking this method may result in the creation of a new code value.</strong> 359 * If this is not desired, use {@link #valueOf(Class, String, Function)} instead.</p> 360 * 361 * @param <T> the compile-time type given as the {@code codeType} parameter. 362 * @param codeType the type of code list. 363 * @param name the name of the code to obtain (case sensitive), or {@code null}. 364 * @return a code matching the given name (possible a new code), or {@code null} if the given name is null. 365 * 366 * @deprecated This method depends on reflection, which is restricted in the context of Java Module System. 367 * Use {@link #valueOf(Class, String, Function)} instead. 368 */ 369 @Deprecated(since="3.1", forRemoval=true) 370 public static <T extends CodeList<T>> T valueOf(final Class<T> codeType, String name) { 371 if (name == null) { 372 return null; 373 } 374 name = name.trim(); 375 final String n = name.trim(); // Need final for lambda. 376 return valueOf(codeType, new Filter() { 377 @Override public boolean accept(CodeList<?> code) { 378 return n.equals(code.name); 379 } 380 381 @Override public String codename() { 382 return n; 383 } 384 }); 385 } 386 387 /** 388 * Returns the code of the given type that matches the given criterion, or returns a new one 389 * if none match it. More specifically, this methods returns the first element (in declaration 390 * order) of the given class where <code>filter.{@linkplain Filter#accept accept}(code)</code> 391 * returns {@code true}. If no such element is found, then there is a choice: 392 * 393 * <ul> 394 * <li>If {@link Filter#codename()} returns {@code null}, then this method returns {@code null}.</li> 395 * <li>Otherwise a new instance is created using the constructor expecting a single {@link String} 396 * argument, which is given the value returned by {@code codename()}.</li> 397 * </ul> 398 * 399 * @param <T> the compile-time type given as the {@code codeType} parameter. 400 * @param codeType the type of code list. 401 * @param filter the criterion for the code to obtain. 402 * @return a code matching the given criterion, or {@code null} if there is no match and 403 * {@link Filter#codename()} returns {@code null}. 404 * 405 * @deprecated This method depends on reflection, which is restricted in the context of Java Module System. 406 * Use {@link #valueOf(Class, String, Function)} instead. 407 */ 408 @Deprecated(since="3.1", forRemoval=true) 409 public static <T extends CodeList<T>> T valueOf(final Class<T> codeType, final Filter filter) { 410 final Elements values = getOrCreateList(codeType); 411 /* 412 * At this point we got the list of all code list values. Now search for a value matching 413 * the filter specified to this method. The search and, eventually, the code creation are 414 * done in the same synchronized block for making sure that the same code is not created 415 * twice concurrently. 416 */ 417 synchronized (values) { 418 for (int i=0; i<values.size(); i++) { 419 final CodeList<?> code = values.resolve(codeType, i); 420 if (filter.accept(code)) { 421 return codeType.cast(code); 422 } 423 } 424 final String nameIfNew = filter.codename(); 425 if (nameIfNew == null || Modifier.isAbstract(codeType.getModifiers())) { 426 return null; 427 } 428 /* 429 * No value value found, but the caller allows us to create a new value. 430 * We need access to the constructor, which may not be public. 431 */ 432 try { 433 final Constructor<T> constructor = codeType.getDeclaredConstructor(String.class); 434 if (!Modifier.isPublic(constructor.getModifiers())) { 435 constructor.setAccessible(true); 436 } 437 T code = constructor.newInstance(nameIfNew); 438 values.set(code.ordinal(), code); 439 return code; 440 } catch (ReflectiveOperationException exception) { 441 throw new IllegalArgumentException("Cannot create code of type " + codeType.getSimpleName(), exception); 442 } 443 } 444 } 445 446 /** 447 * Returns the code of the given type and name if it exists, or optionally creates a new code. 448 * This method returns the first instance (in declaration order) of the given class for which the 449 * {@linkplain #name() name} is {@linkplain String#equalsIgnoreCase(String) equals, ignoring case}, 450 * to the given name. If no such instance is found, then there is a choice: 451 * 452 * <ul> 453 * <li>If {@code constructor} is {@code null}, then this method returns an empty value.</li> 454 * <li>Otherwise a {@linkplain Optional#ofNullable(Object) nullable} new instance is created 455 * by a call to {@code constructor.apply(name)}.</li> 456 * </ul> 457 * 458 * This method can be used for the implementation of {@code valueOf(String)} in subclasses. 459 * See the "Guidance for subclasses" section in the class Javadoc. 460 * 461 * <h4>Name matching criterion</h4> 462 * As of GeoAPI 3.1, names are compared using {@link String#equalsIgnoreCase(String)}. 463 * This is different than GeoAPI 3.0, which compared names using {@link String#equals(Object)}. 464 * Being case-insensitive allows to recognize some UML identifiers as equivalent to the names 465 * of constants declared in {@code CodeList} subclasses. 466 * 467 * @param <E> the compile-time type given as the {@code codeType} parameter. 468 * @param codeType the type of the code list for which to get an element. 469 * @param name the name of the code to obtain (case-insensitive), or {@code null}. 470 * @param constructor the constructor to use if a new code needs to be created, 471 * or {@code null} for not creating any new code. 472 * @return a code matching the given name, or empty if the given name is null or blank, 473 * or if no existing code matches the given name and {@code constructor} is null or returned a null value. 474 * 475 * @since 3.1 476 */ 477 public static <E extends CodeList<E>> Optional<E> valueOf(final Class<E> codeType, 478 String name, final Function<? super String, ? extends E> constructor) 479 { 480 if (name != null && !(name = name.trim()).isBlank()) { 481 final Elements values = valueList(codeType); 482 /* 483 * The search and, eventually, the code creation are done in the same synchronized 484 * block for making sure that the same code is not created twice concurrently. 485 */ 486 synchronized (values) { 487 final int size = values.size(); 488 for (int i=0; i<size; i++) { 489 final E element; 490 final Object code = values.get(i); 491 if (code instanceof String) { 492 if (!name.equalsIgnoreCase((String) code)) continue; 493 element = values.resolve(codeType, i); 494 } else { 495 element = codeType.cast(code); 496 } 497 if (name.equalsIgnoreCase(element.name())) { 498 return Optional.of(element); 499 } 500 } 501 if (constructor != null) { 502 final E element = constructor.apply(name); 503 if (element != null) { 504 values.set(element.ordinal(), element); 505 return Optional.of(element); 506 } 507 } 508 } 509 } 510 return Optional.empty(); 511 } 512 513 /** 514 * Returns the list of code list names or instances for the specified type of code list. 515 * Callers shall use the returned list in a block synchronized on the list instance. 516 * 517 * @param codeType the type of code list for which to get the current code list values. 518 * @return all current values for the given type (never {@code null}). 519 */ 520 private static Elements valueList(final Class<? extends CodeList<?>> codeType) { 521 Elements values; 522 synchronized (VALUES) { 523 values = VALUES.get(codeType); 524 } 525 if (values == null) { 526 /* 527 * If no list has been found for the given type, maybe the class was not yet initialized. 528 * Try to force initialization of the given class in order to register its list of static 529 * final constants, then check again. 530 */ 531 final String classname = Objects.requireNonNull(codeType, "The codeType argument shall not be null.").getName(); 532 try { 533 Class.forName(classname, true, codeType.getClassLoader()); 534 } catch (ClassNotFoundException e) { 535 throw new TypeNotPresentException(classname, e); // Should never happen. 536 } 537 values = getOrCreateList(codeType); 538 } 539 return values; 540 } 541 542 /** 543 * Returns the values for the specified type of code list. 544 * 545 * @param <E> the compile-time type given as the {@code codeType} parameter. 546 * @param codeType the type of code list for which to get the current code list values. 547 * @return all current values for the given type (never {@code null}). 548 * 549 * @since 3.1 550 */ 551 @SuppressWarnings("unchecked") 552 public static <E extends CodeList<E>> E[] values(final Class<E> codeType) { 553 return valueList(codeType).toArray(codeType); 554 } 555 556 /** 557 * Returns the list of codes of the same kind as this code. 558 * Invoking this method gives identical results than invoking the static {@code values()} methods 559 * provided in {@code CodeList} subclasses, except that {@code family()} does not require the class 560 * to be known at compile-time — provided that at least one instance of the family is available. 561 * 562 * @return the codes of the same kind as this code. 563 */ 564 @Override 565 public abstract E[] family(); 566 // We do not provide default implementation because casting to `CodeList<E>` is unsafe. 567 568 /** 569 * Returns the programmatic name of this code list element. 570 * If this element is a constant defined in a {@code CodeList} subclass, 571 * then this is the name of the public static field for that constant. 572 * 573 * @return the name of this code constant. 574 */ 575 @Override 576 public final String name() { 577 return name; 578 } 579 580 /** 581 * Returns the identifier declared in the UML annotation. 582 * The UML identifier shall be the ISO or OGC name for this code constant. 583 * 584 * @return the ISO/OGC identifier for this code. 585 * 586 * @see UML#identifier() 587 */ 588 @Override 589 public Optional<String> identifier() { 590 /* 591 * Save the field in a local variable for protection against concurrent change (this 592 * operation is guaranteed atomic according Java specification). We don't synchronize 593 * because it is not a problem if this method is executed twice in concurrent threads. 594 */ 595 String id = identifier; 596 if (id == null) { 597 id = ""; 598 try { 599 final Field field = getClass().getField(name); 600 if (Modifier.isStatic(field.getModifiers())) { 601 boolean valid; 602 try { 603 valid = equals(field.get(null)); 604 } catch (IllegalAccessException e) { 605 /* 606 * Should never happen with accessible packages because `getField(String)` returns 607 * only public fields. However, it may happen if the code list is defined in a user 608 * module and that module does not export the package containing the code list. 609 * In such case, we have to trust the name provided in this code list instance. 610 */ 611 valid = true; 612 System.getLogger(Errors.LOGGER).log(System.Logger.Level.DEBUG, this::cannotReadField, e); 613 } 614 if (valid) { 615 // Fetching annotations is allowed even with non-exported packages. 616 final UML annotation = field.getAnnotation(UML.class); 617 if (annotation != null) { 618 id = annotation.identifier().intern(); 619 } 620 } 621 } 622 } catch (NoSuchFieldException e) { 623 /* 624 * There is no field for a code of this name. It may be normal, because the user 625 * may have created a custom `CodeList` without declaring it as a constant. 626 */ 627 System.getLogger(Errors.LOGGER).log(System.Logger.Level.TRACE, this::cannotReadField, e); 628 } 629 identifier = id; 630 } 631 return id.isEmpty() ? Optional.empty() : Optional.of(id); 632 } 633 634 /** 635 * Produces the message for a field that cannot be read by reflection. 636 * This is used for logging purposes. 637 */ 638 private String cannotReadField() { 639 return Elements.cannotRead(getClass(), name); 640 } 641 642 /** 643 * Returns the ordinal of this code element. This is the index of this element in the array returned 644 * by {@link #family()}. Those elements are in the order of the constants declared in subclasses, 645 * where the first element is assigned an ordinal value of zero. 646 * 647 * <h4>Stability</h4> 648 * For a given GeoAPI version, 649 * the code list elements declared as constants in subclasses always have the same ordinal values. 650 * However, the ordinal value of the same constant may differ between different GeoAPI versions 651 * if new elements were inserted or deleted between existing elements. 652 * User-defined elements (created by calls to a {@code valueOf(…)} method) are not guaranteed 653 * to have the same ordinal value between different execution of the same application, 654 * because these values depend on the order in which code list elements are created. 655 * 656 * @return the position of this code in elements declaration. 657 */ 658 @Override 659 public final int ordinal() { 660 return ordinal; 661 } 662 663 /** 664 * Compares this code with the specified object for order. 665 * Returns a negative integer, zero, or a positive integer as this object 666 * is less than, equal to, or greater than the specified object. 667 * 668 * <p>Code list constants are only comparable to other code list constants of the same type. 669 * The natural order implemented by this method is the order in which the constants are declared.</p> 670 * 671 * @param other the code constant to compare with this code. 672 * @return a negative value if the given code is less than this code, 673 * a positive value if greater or 0 if equal. 674 */ 675 @Override 676 @SuppressWarnings("rawtypes") 677 public final int compareTo(final E other) { 678 final Class<? extends CodeList> ct = this.getClass(); 679 final Class<? extends CodeList> co = other.getClass(); 680 if (!ct.equals(co)) { 681 throw new ClassCastException("Cannot compare " + ct.getSimpleName() + " to " + co.getSimpleName() + '.'); 682 } 683 return Integer.compare(ordinal, other.ordinal()); 684 } 685 686 /* 687 * Do not define `equals(Object)` and `hashCode()`. The identity comparison is consistent with above 688 * `compareTo(E)` method because there is no two CodeLists of the same class having the same ordinal value. 689 */ 690 691 /** 692 * {@return a string representation of this code list}. 693 */ 694 @Override 695 public String toString() { 696 return getClass().getSimpleName() + '.' + name; 697 } 698 699 /** 700 * Resolves the code list to an unique instance after deserialization. 701 * The instance shall be resolved using its {@linkplain #name() name}, not its {@linkplain #ordinal() ordinal}, 702 * because the latter value is not guaranteed to be stable between different GeoAPI versions or, 703 * in the case of user-defined code list elements, between different JVM executions. 704 * 705 * <p>The default implementation tries to replace the deserialized instance by the result of 706 * <code>{@link #valueOf(Class, String, Function) valueOf}(getClass(), name(), null)</code>. 707 * If {@code valueOf(…)} returned an empty value, then {@code this} is updated with a new 708 * ordinal value and added to the list of codes associated to its class.</p> 709 * 710 * @return either {@code this} or an existing code list for the same name (ignoring case). 711 * @throws ObjectStreamException if the deserialization failed. 712 */ 713 protected Object readResolve() throws ObjectStreamException { 714 /* 715 * Casted to <E> in order to satisfy the compiler, but this code should be safe 716 * even if the class is not really `Class<E>` because this method returns `Object`. 717 */ 718 @SuppressWarnings("unchecked") 719 final Class<E> codeType = (Class<E>) getClass(); 720 final E element = valueOf(codeType, name, null).orElse(null); 721 if (element != null) { 722 return element; 723 } 724 final Elements values = getOrCreateList(codeType); 725 synchronized (values) { 726 ordinal = values.size(); 727 values.add(this); 728 } 729 return this; 730 } 731}