Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -60,7 +60,7 @@ public final class ModifiableIdentifierM /** * Sets the {@code xlink:href} value, which may be null. If an explicit {@code xlink:href} identifier exists, - * then it will removed before to set the new {@code href} in the {@link XLink} object. The intend is to give + * then it will removed before to set the new {@code href} in the {@link XLink} object. The intent is to give * precedence to the {@link XLink#getHRef()} property in every cases where the {@code href} is parsable as a * {@link URI}, and use the value associated to the {@code HREF} key only as a fallback when the string can not * be parsed.
Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -157,7 +157,7 @@ public abstract class PropertyType<Value * @param value the primitive type wrapper. * @param mayBeNil {@code true} if we should check for nil reasons. */ - PropertyType(final BoundType value, final boolean mayBeNil) { + protected PropertyType(final BoundType value, final boolean mayBeNil) { metadata = value; if (mayBeNil) { final Object property = PrimitiveTypeProperties.property(value); Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DataDirectory.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -84,31 +84,39 @@ public enum DataDirectory { private Path directory; /** + * Prevents the log message about {@code SIS_DATA} environment variable not set. + * This is used for the "About" command line action only. + */ + public static void quiet() { + lastWarning = Messages.Keys.DataDirectoryNotSpecified_1; + } + + /** * Logs a message to the {@code "org.apache.sis.system"} logger only if different than the last warning. */ - private static void warning(final String method, final Exception e, final short key, final Object... parameters) { + private static void warning(final Exception e, final short key, final Object... parameters) { if (key != lastWarning) { lastWarning = key; - log(Level.WARNING, method, e, key, parameters); + log(Level.WARNING, e, key, parameters); } } /** * Logs a message to the {@code "org.apache.sis.system"} logger. */ - private static void log(final Level level, final String method, final Exception e, final short key, final Object... parameters) { + private static void log(final Level level, final Exception e, final short key, final Object... parameters) { final LogRecord record = Messages.getResources(null).getLogRecord(level, key, parameters); record.setLoggerName(Loggers.SYSTEM); if (e != null) { record.setThrown(e); } - Logging.log(DataDirectory.class, method, record); + Logging.log(null, null, record); // Let Logging.log(…) infers the public caller. } /** * Returns the value of {@value #ENV} environment variable, or {@code null} if none. * This method does not perform any logging and does not verify if the directory exists. - * If the intend is to perform I/O operations, use {@link #getRootDirectory()} instead. + * If the intent is to perform I/O operations, use {@link #getRootDirectory()} instead. * * @return the {@value #ENV} environment variable, or {@code null} if none. * @throws SecurityException if this method is not allowed to query the environment variable. @@ -149,22 +157,22 @@ public enum DataDirectory { if (rootDirectory == null) try { final String dir = getenv(); if (dir == null || dir.isEmpty()) { - warning("getRootDirectory", null, Messages.Keys.DataDirectoryNotSpecified_1, ENV); + warning(null, Messages.Keys.DataDirectoryNotSpecified_1, ENV); } else try { final Path path = Paths.get(dir); if (!Files.isDirectory(path)) { - warning("getRootDirectory", null, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, path); + warning(null, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, path); } else if (!Files.isReadable(path)) { - warning("getRootDirectory", null, Messages.Keys.DataDirectoryNotReadable_2, ENV, path); + warning(null, Messages.Keys.DataDirectoryNotReadable_2, ENV, path); } else { - log(Level.CONFIG, "getRootDirectory", null, Messages.Keys.DataDirectory_2, ENV, path); + log(Level.CONFIG, null, Messages.Keys.DataDirectory_2, ENV, path); rootDirectory = path; } } catch (InvalidPathException e) { - warning("getRootDirectory", e, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, dir); + warning(e, Messages.Keys.DataDirectoryDoesNotExist_2, ENV, dir); } } catch (SecurityException e) { - warning("getRootDirectory", e, Messages.Keys.DataDirectoryNotAuthorized_1, ENV); + warning(e, Messages.Keys.DataDirectoryNotAuthorized_1, ENV); } return rootDirectory; } @@ -194,12 +202,12 @@ public enum DataDirectory { } else if (Files.isWritable(root)) try { directory = Files.createDirectory(dir); } catch (IOException e) { - warning("getDirectory", e, Messages.Keys.DataDirectoryNotWritable_2, ENV, root); + warning(e, Messages.Keys.DataDirectoryNotWritable_2, ENV, root); } else { - warning("getDirectory", null, Messages.Keys.DataDirectoryNotWritable_2, ENV, root); + warning(null, Messages.Keys.DataDirectoryNotWritable_2, ENV, root); } } catch (SecurityException e) { - warning("getDirectory", e, Messages.Keys.DataDirectoryNotAccessible_2, ENV, name); + warning(e, Messages.Keys.DataDirectoryNotAccessible_2, ENV, name); } } } Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/DefaultFactories.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -195,7 +195,7 @@ public final class DefaultFactories exte * is equal or is a child of the SIS loader, then it is left unchanged. Otherwise the context * class loader is replaced by the SIS one. * - * <p>The intend of this method is to ensure that {@link ServiceLoader#load(Class)} will find the + * <p>The intent of this method is to ensure that {@link ServiceLoader#load(Class)} will find the * Apache SIS services even in an environment that defined an unsuitable context class loader.</p> * * @return the context class loader if suitable, or another class loader otherwise. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Loggers.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -145,7 +145,7 @@ public final class Loggers extends Stati } } /* - * Process the loggers in alphabetical order. The intend is to process parent loggers before child. + * Process the loggers in alphabetical order. The intent is to process parent loggers before child. * The first logger in the map should be the SIS root logger, "org.apache.sis". */ final Iterator<Map.Entry<String,Level>> it = levels.entrySet().iterator(); Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -87,14 +87,14 @@ public final class Modules { * * @see org.apache.sis.util.Version */ - public static final int MAJOR_VERSION = 0; + public static final int MAJOR_VERSION = 1; /** * The minor version number of all Apache SIS modules. * * @see org.apache.sis.util.Version */ - public static final int MINOR_VERSION = 8; + public static final int MINOR_VERSION = 0; /** * The prefix of all classnames in Apache SIS, including a trailing dot. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -58,7 +58,7 @@ public final class Semaphores { /** * A flag to indicate that {@link org.apache.sis.referencing.operation.AbstractCoordinateOperation} * is querying parameters of a {@code MathTransform} enclosed in the operation. This is often in the - * intend to format WKT of a {@code "ProjectedCRS"} element. + * intent to format WKT of a {@code "ProjectedCRS"} element. */ public static final int ENCLOSED_IN_OPERATION = 8; Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/system/Threads.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -22,7 +22,7 @@ import org.apache.sis.util.logging.Loggi /** * Utilities methods for threads. This class declares in a single place every {@link ThreadGroup} used in SIS. - * Their intend is to bring some order in debugger informations, by grouping the threads created by SIS together + * Their intent is to bring some order in debugger informations, by grouping the threads created by SIS together * under the same parent tree node. * * <div class="section">Note on dependencies</div> @@ -39,7 +39,7 @@ final class Threads extends Static { /** * The parent of every threads declared in this class. This parent will be declared as close * as possible to the root of all thread groups (i.e. not as an application thread subgroup). - * The intend is to separate the library thread groups from the user application thread groups. + * The intent is to separate the library thread groups from the user application thread groups. */ static final ThreadGroup SIS; static { Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/Citations.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -38,7 +38,7 @@ import static org.apache.sis.util.iso.De * implementation is defined here since it is needed by some utility methods. * * @author Martin Desruisseaux (Geomatys) - * @version 0.7 + * @version 1.0 * @since 0.3 * @module */ @@ -247,25 +247,49 @@ public final class Citations extends Sta */ public static boolean identifierMatches(final Citation citation, final Identifier identifier, final CharSequence code) { if (citation != null && code != null) { - final Iterator<? extends Identifier> identifiers = iterator(citation.getIdentifiers()); - if (identifiers == null) { + final Collection<? extends Identifier> citIds = citation.getIdentifiers(); + Iterator<? extends Identifier> it = iterator(citIds); + if (it == null) { return titleMatches(citation, code); } - while (identifiers.hasNext()) { - final Identifier id = identifiers.next(); - if (id != null && equalsFiltered(code, id.getCode())) { + while (it.hasNext()) { + final Identifier citId = it.next(); + if (citId != null && equalsFiltered(code, citId.getCode())) { + /* + * Found a possible match. We will take the code space in account only if it is defined + * by both identifiers. If a code space is undefined, we consider that we have a match. + */ if (identifier != null) { final String codeSpace = identifier.getCodeSpace(); if (codeSpace != null) { - final String cs = id.getCodeSpace(); - if (cs != null) { - return equalsFiltered(codeSpace, cs); + final String cs = citId.getCodeSpace(); + if (cs != null && !equalsFiltered(codeSpace, cs)) { + continue; // Check other identifiers. } } } return true; } } + /* + * Before to give up, maybe the given code argument is actually written using a "codeSpace:code" syntax. + * Try to parse that syntax only if no Identifier argument were specified (otherwise we require the code + * and code space to be splitted as defined in the identifier). + */ + if (identifier == null) { + int s = 0; + final int length = code.length(); + while ((s = CharSequences.indexOf(code, DEFAULT_SEPARATOR, s, length)) >= 0) { + final CharSequence codeSpace = code.subSequence(0, s); + final CharSequence localPart = code.subSequence(++s, length); + for (it = citIds.iterator(); it.hasNext();) { + final Identifier id = it.next(); + if (equalsFiltered(codeSpace, id.getCodeSpace()) && equalsFiltered(localPart, id.getCode())) { + return true; + } + } + } + } } return false; } @@ -459,7 +483,7 @@ public final class Citations extends Sta * <div class="section">When to use</div> * Use this method when assigning values to be returned by methods like {@link Identifier#getCodeSpace()}, * since those values are likely to be compared without special care about ignorable identifier characters. - * But if the intend is to format a more complex string like WKT or {@code toString()}, then we suggest to + * But if the intent is to format a more complex string like WKT or {@code toString()}, then we suggest to * use {@code getIdentifier(citation, true)} instead, which will produce the same result but preserving the * ignorable characters, which can be useful for formatting purpose. * @@ -473,7 +497,16 @@ public final class Citations extends Sta * after we moved the {@code sis-utility} code that use this method. */ public static String getUnicodeIdentifier(final Citation citation) { - final String identifier = getIdentifier(citation, true); + return removeIgnorableCharacters(getIdentifier(citation, true)); + } + + /** + * Removes characters that are ignorable according Unicode specification. + * + * @param identifier the character sequence from which to remove ignorable characters, or {@code null}. + * @return a character sequence with ignorable character removed. May be the same instance than the given argument. + */ + public static String removeIgnorableCharacters(final String identifier) { if (identifier != null) { /* * First perform a quick check to see if there is any ignorable characters. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -18,6 +18,7 @@ package org.apache.sis.internal.util; import java.util.*; import java.lang.reflect.Array; +import java.util.function.Predicate; import org.opengis.util.CodeList; import org.apache.sis.util.Static; import org.apache.sis.util.Numbers; @@ -28,9 +29,6 @@ import org.opengis.parameter.InvalidPara import static org.apache.sis.util.collection.Containers.hashMapCapacity; -// Branch-dependent imports -import java.util.function.Predicate; - /** * Static methods working on {@link Collection} objects. @@ -588,7 +586,7 @@ public final class CollectionsExt extend /** * Returns a more compact representation of the given map. This method is similar to * {@link #unmodifiableOrCopy(Map)} except that it does not wrap the map in an unmodifiable - * view. The intend is to avoid one level of indirection for performance and memory reasons. + * view. The intent is to avoid one level of indirection for performance and memory reasons. * This is okay only if the map is kept in a private field and never escape outside that class. * * @param <K> the type of keys in the map. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -345,7 +345,7 @@ public final class DoubleDouble extends * SIS often creates matrices for unit conversions, and most conversion factors are defined precisely in base 10. * For example the conversion from feet to metres is defined by a factor of exactly 0.3048, which can not be * represented precisely as a {@code double}. Consequently if a value of 0.3048 is given, we can assume that - * the intend was to provide the "feet to metres" conversion factor and complete the double-double instance + * the intent was to provide the "feet to metres" conversion factor and complete the double-double instance * accordingly. * * @param value the value for which to get this error. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/EmptyQueue.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/EmptyQueue.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/EmptyQueue.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/EmptyQueue.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -43,7 +43,7 @@ final class EmptyQueue<E> extends Abstra /** * The singleton instance to be returned by {@link CollectionsExt#emptyQueue()}. - * This is not parameterized on intend. + * This is not parameterized on intent. */ @SuppressWarnings("rawtypes") static final Queue INSTANCE = new EmptyQueue(); Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/internal/util/MetadataServices.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -76,7 +76,7 @@ public class MetadataServices extends Op if (c == null) { /* * Double-checked locking: okay since Java 5 provided that the 'instance' field is volatile. - * In the particular case of this class, the intend is to ensure that SystemListener.add(…) + * In the particular case of this class, the intent is to ensure that SystemListener.add(…) * is invoked only once. */ c = getInstance(MetadataServices.class, Modules.UTILITIES, "sis-metadata", Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/Appender.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/Appender.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/Appender.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/Appender.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -133,7 +133,7 @@ abstract class Appender implements Appen /** * If the given sequence begins with a low surrogate completing a previous high surrogate, - * delegates to {@link #append(char)} and returns {@code start+1}. The intend is to avoid + * delegates to {@link #append(char)} and returns {@code start+1}. The intent is to avoid * processing a character sequence which starts by an invalid code point. * * @param sequence the character sequence to write. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -22,6 +22,7 @@ import java.util.Locale; import java.util.TimeZone; import java.util.Date; import java.io.IOException; +import java.io.UncheckedIOException; import java.text.Format; import java.text.DateFormat; import java.text.NumberFormat; @@ -46,9 +47,6 @@ import org.apache.sis.internal.util.Loca import static org.apache.sis.internal.util.StandardDateFormat.UTC; -// Branch-dependent imports -import java.io.UncheckedIOException; - /** * Base class of {@link Format} implementations which delegate part of their work to other Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Statistics.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -18,6 +18,8 @@ package org.apache.sis.math; import java.util.Objects; import java.io.Serializable; +import java.util.function.LongConsumer; +import java.util.function.DoubleConsumer; import org.opengis.util.InternationalString; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.iso.Types; @@ -27,10 +29,6 @@ import static java.lang.Double.NaN; import static java.lang.Double.isNaN; import static java.lang.Double.doubleToLongBits; -// Branch-dependent imports -import java.util.function.LongConsumer; -import java.util.function.DoubleConsumer; - /** * Holds some statistics derived from a series of sample values. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -520,7 +520,7 @@ public abstract class Vector extends Abs * * doubleValue(i) = first + increment*i * - * The intend is that if tolerance = 0 and this method returns a non-null value, then replacing + * The intent is that if tolerance = 0 and this method returns a non-null value, then replacing * this vector by an instance of SequenceVector should produce exactely the same double values. */ if (type >= Numbers.FLOAT && type <= Numbers.DOUBLE) { Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AbstractUnit.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -73,7 +73,7 @@ abstract class AbstractUnit<Q extends Qu * (<strong>not</strong> the UCUM format). In particular, this symbol uses Unicode characters * for arithmetic operators and superscripts, as in “m/s²”. However this symbol should never * contains the unit conversion terms. For example “km” is okay, but “1000⋅m” is not. - * The intend of those rules is to make easier to analyze the symbol in methods like + * The intent of those rules is to make easier to analyze the symbol in methods like * {@link ConventionalUnit#power(String)}. * * <p>Users can override this symbol by call to {@link UnitFormat#label(Unit, String)}, Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -991,7 +991,7 @@ public class AngleFormat extends Format minutes = Math.abs(degrees - (degrees = truncate(degrees))) * 60; /* * Limit the maximal number of fraction digits to the amount of significant digits for a 'double' value. - * The intend is to avoid non-significant garbage that are pure artifacts from the conversion from base + * The intent is to avoid non-significant garbage that are pure artifacts from the conversion from base * 2 to base 10. */ final int n = fractionDigitsForDelta(Math.ulp(angle) * (secondsFieldWidth == 0 ? 60 : 3600), false); Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/UnitFormat.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -459,7 +459,7 @@ public class UnitFormat extends Format i * Returns the unit instance for the given long (un)localized or name. * This method is somewhat the converse of {@link #symbolToName()}, but recognizes also * international and American spelling of unit names in addition of localized names. - * The intend is to recognize "meter" as well as "metre". + * The intent is to recognize "meter" as well as "metre". * * <p>While we said that {@code UnitFormat} is not thread safe, we make an exception for this method * for allowing the singleton {@link #INSTANCE} to parse symbols in a multi-threads environment.</p> Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -27,18 +27,14 @@ import org.opengis.geometry.DirectPositi import org.opengis.referencing.cs.AxisDirection; // For javadoc import org.apache.sis.util.Static; -import org.apache.sis.util.Workaround; import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.util.Constants; -import static java.lang.Math.PI; -import static java.lang.Math.abs; import static org.apache.sis.measure.UnitRegistry.SI; import static org.apache.sis.measure.UnitRegistry.ACCEPTED; import static org.apache.sis.measure.UnitRegistry.IMPERIAL; import static org.apache.sis.measure.UnitRegistry.OTHER; import static org.apache.sis.measure.UnitRegistry.PREFIXABLE; -import static org.apache.sis.measure.SexagesimalConverter.EPS; /** @@ -1117,7 +1113,7 @@ public final class Units extends Static final LinearConverter ten4 = LinearConverter.scale(10000, 1); /* * All Unit<Angle>. - * 20 is the greatest common denominator between 180 and 200. The intend is to have arguments as small + * 20 is the greatest common denominator between 180 and 200. The intent is to have arguments as small * as possible in the call to the scale(double, double) method, while keeping the right side integer. * Staying closer to zero during conversions helo to reduce rounding errors. */ @@ -1474,51 +1470,6 @@ public final class Units extends Static } /** - * Multiplies the given unit by the given factor. For example multiplying {@link #METRE} - * by 1000 gives {@link #KILOMETRE}. Invoking this method is equivalent to invoking - * {@link Unit#multiply(double)} except for the following: - * - * <ul> - * <li>A small tolerance factor is applied for a few factors commonly used in GIS. - * For example {@code multiply(RADIANS, 0.0174532925199...)} will return {@link #DEGREE} - * even if the given numerical value is slightly different than {@linkplain Math#PI π}/180. - * The tolerance factor and the set of units handled especially may change in future SIS versions.</li> - * <li>This method tries to returns unique instances for some common units.</li> - * </ul> - * - * @param <Q> the quantity measured by the unit. - * @param unit the unit to multiply. - * @param factor the multiplication factor. - * @return the unit multiplied by the given factor. - * - * @deprecated Replaced by Apache SIS implementation of {@link Unit#multiply(double)}. - */ - @Deprecated - @Workaround(library="JSR-275", version="0.9.3") - @SuppressWarnings("unchecked") - public static <Q extends Quantity<Q>> Unit<Q> multiply(Unit<Q> unit, final double factor) { - if (RADIAN.equals(unit)) { - if (abs(factor - (PI / 180)) <= (EPS * PI/180)) { - return (Unit<Q>) DEGREE; - } - if (abs(factor - (PI / 200)) <= (EPS * PI/200)) { - return (Unit<Q>) GRAD; - } - } else if (METRE.equals(unit)) { - if (abs(factor - 0.3048) <= (EPS * 0.3048)) { - return (Unit<Q>) FOOT; - } - if (abs(factor - (1200.0/3937)) <= (EPS * (1200.0/3937))) { - return (Unit<Q>) US_SURVEY_FOOT; - } - } - if (abs(factor - 1) > EPS) { - unit = unit.multiply(factor); - } - return unit; - } - - /** * Returns the factor by which to multiply the standard unit in order to get the given unit. * The "standard" unit is usually the SI unit on which the given unit is based, as given by * {@link Unit#getSystemUnit()}. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -75,16 +75,6 @@ public class OptionKey<T> implements Ser private static final long serialVersionUID = -7580514229639750246L; /** - * The library to use for creating geometric objects at reading time. - * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D. - * If this option is not specified, then a default library will be selected among - * the libraries available in the runtime environment. - * - * @since 0.8 - */ - public static final OptionKey<GeometryLibrary> GEOMETRY_LIBRARY = new OptionKey<>("GEOMETRY_LIBRARY", GeometryLibrary.class); - - /** * The locale to use for locale-sensitive data. This option determines the language to use for writing * {@link org.apache.sis.util.iso.AbstractInternationalString international strings} when the target * storage support only one language. It may also control number and date patterns in some file formats @@ -192,6 +182,16 @@ public class OptionKey<T> implements Ser public static final OptionKey<ByteBuffer> BYTE_BUFFER = new OptionKey<>("BYTE_BUFFER", ByteBuffer.class); /** + * The library to use for creating geometric objects at reading time. + * Some libraries are the Java Topology Suite (JTS), ESRI geometry API and Java2D. + * If this option is not specified, then a default library will be selected among + * the libraries available in the runtime environment. + * + * @since 0.8 + */ + public static final OptionKey<GeometryLibrary> GEOMETRY_LIBRARY = new OptionKey<>("GEOMETRY_LIBRARY", GeometryLibrary.class); + + /** * The number of spaces to use for indentation when formatting text files in WKT or XML formats. * A value of {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE} means to format the whole WKT * or XML document on a single line without line feeds or indentation. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/setup/package-info.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -17,9 +17,13 @@ /** * Provides information and some controls about SIS configuration. + * System-wide informations are provided by the {@link org.apache.sis.setup.About} class. + * Some controls, for example on the {@linkplain org.apache.sis.setup.GeometryLibrary geometry library} to use, + * is provided by a set of {@linkplain org.apache.sis.setup.OptionKey options} which can be used when a data store + * is created. * * @author Martin Desruisseaux (Geomatys) - * @version 0.7 + * @version 0.8 * @since 0.3 * @module */ Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -20,15 +20,13 @@ import java.util.List; import java.util.ArrayList; import java.util.Locale; import java.sql.SQLException; +import java.io.UncheckedIOException; import java.nio.file.DirectoryIteratorException; import java.lang.reflect.InvocationTargetException; import org.opengis.util.InternationalString; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.util.collection.BackingStoreException; -// Branch-dependent imports -import java.io.UncheckedIOException; - /** * Static methods working with {@link Exception} instances. Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ObjectConverter.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ObjectConverter.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ObjectConverter.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/ObjectConverter.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -17,10 +17,8 @@ package org.apache.sis.util; import java.util.Set; -import org.apache.sis.math.FunctionProperty; - -// Branch-dependent imports import java.util.function.Function; +import org.apache.sis.math.FunctionProperty; /** Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/StringBuilders.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -197,7 +197,7 @@ public final class StringBuilders extend /** * A sequence of a constant character. This implementation does not perform any argument * check since it is for {@link StringBuilder#append(CharSequence, int, int)} usage only. - * The intend is to allow {@code StringBuilder} to append the characters in one operation + * The intent is to allow {@code StringBuilder} to append the characters in one operation * instead than looping on {@link StringBuilder#insert(int, char)} (which would require * memory move on each call). */ Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -21,6 +21,9 @@ import java.util.Set; import java.util.Iterator; import java.util.AbstractMap; import java.util.LinkedHashMap; +import java.util.function.Supplier; +import java.util.function.Function; +import java.util.function.BiFunction; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; @@ -35,14 +38,33 @@ import org.apache.sis.internal.system.De import org.apache.sis.internal.system.DelayedExecutor; import org.apache.sis.internal.system.ReferenceQueueConsumer; -// Branch-dependent imports -import java.util.function.Supplier; - /** - * A concurrent cache mechanism. This implementation is thread-safe and supports concurrency. - * A cache entry can be locked when an object is in process of being created. The steps - * are as below: + * A concurrent map capable to locks entries for which the value is in process of being computed. + * This map is intended for use as a cache, with a goal of avoiding to compute the same values twice. + * This implementation is thread-safe and supports concurrency. + * {@code Cache} is based on {@link ConcurrentHashMap} with the addition of three main capabilities: + * + * <ul> + * <li>Lock an entry when its value is under computation in a thread.</li> + * <li>Block other threads requesting the value of that particular entry until computation is completed.</li> + * <li>Retain oldest values by soft or weak references instead of strong references.</li> + * </ul> + * + * The easiest way to use this class is to invoke {@link #computeIfAbsent computeIfAbsent(…)} + * or {@link #getOrCreate getOrCreate(…)} with lambda functions as below: + * + * {@preformat java + * private final Cache<String,MyObject> cache = new Cache<String,MyObject>(); + * + * public MyObject getMyObject(String key) { + * return cache.computeIfAbsent(key, (k) -> createMyObject(k)); + * } + * } + * + * Alternatively, one can handle explicitely the locks. + * This alternative sometime provides more flexibility, for example in exception handling. + * The steps are as below: * * <ol> * <li>Check if the value is already available in the map. @@ -53,34 +75,10 @@ import java.util.function.Supplier; * <li>Otherwise compute the value, store the result and release the lock.</li> * </ol> * - * The easiest way (except for exception handling) to use this class is to prepare a - * {@link Callable} statement to be executed only if the object is not in the cache, - * and to invoke the {@link #getOrCreate getOrCreate} method. Example: - * - * {@preformat java - * private final Cache<String,MyObject> cache = new Cache<String,MyObject>(); - * - * public MyObject getMyObject(final String key) throws MyCheckedException { - * try { - * return cache.getOrCreate(key, new Callable<MyObject>() { - * MyObject call() throws FactoryException { - * return createMyObject(key); - * } - * }); - * } catch (MyCheckedException | RuntimeException e) { - * throw e; - * } catch (Exception e) { - * throw new UndeclaredThrowableException(e); - * } - * } - * } - * - * An alternative is to perform explicitly all the steps enumerated above. This alternative - * avoid the creation of a temporary {@code Callable} statement which may never be executed, - * and avoid the exception handling due to the {@code throws Exception} clause. Note that the - * call to {@link Handler#putAndUnlock putAndUnlock} <strong>must</strong> be in the {@code finally} - * block of a {@code try} block beginning immediately after the call to {@link #lock lock}, - * no matter what the result of the computation is (including {@code null}). + * Code example is shown below. + * Note that the call to {@link Handler#putAndUnlock putAndUnlock(…)} <strong>must</strong> + * be inside the {@code finally} block of a {@code try} block beginning immediately after the call + * to {@link #lock lock(…)}, no matter what the result of the computation is (including {@code null}). * * {@preformat java * private final Cache<String,MyObject> cache = new Cache<String,MyObject>(); @@ -130,7 +128,8 @@ import java.util.function.Supplier; * <var>A</var>. If this rule is not meet, deadlock may occur randomly. * * @author Martin Desruisseaux (Geomatys) - * @version 0.4 + * @author Alexis Manin (Geomatys) + * @version 1.0 * * @param <K> the type of key objects. * @param <V> the type of value objects. @@ -138,11 +137,11 @@ import java.util.function.Supplier; * @since 0.3 * @module */ -public class Cache<K,V> extends AbstractMap<K,V> { +public class Cache<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V> { /** - * The map that contains the cached values. If a value is under the process of being - * calculated, then the value will be a temporary instance of {@link Handler}. The - * value may also be weak or soft {@link Reference} objects. + * The map that contains the cached values. If a value is in the process of being computed, + * then the value will be a temporary instance of {@link Handler}. + * The value may also be weak or soft {@link Reference} objects. */ private final ConcurrentMap<K,Object> map; @@ -224,11 +223,11 @@ public class Cache<K,V> extends Abstract */ @Override public void clear() { - map.clear(); - /* - * Do not update "costs" and "totalCost". Instead let adjustReferences(…) - * do its job, which needs to be done in a different thread. - */ + synchronized (costs) { + map.clear(); + costs.clear(); + totalCost = 0; + } } /** @@ -254,14 +253,126 @@ public class Cache<K,V> extends Abstract } /** - * Returns {@code true} if this map contains the specified key. + * Returns the value mapped to the given key in the cache, potentially waiting for computation to complete. + * This method is similar to {@link #peek(Object)} except that it blocks if the value is currently under + * computation in another thread. * - * @param key the key to check for existence. - * @return {@code true} if the given key still exist in this cache. + * @param key the key of the value to get. + * @return the value mapped to the given key, or {@code null} if none. + * + * @see #peek(Object) + * @see #containsKey(Object) + * @see #computeIfAbsent(Object, Function) */ @Override - public boolean containsKey(final Object key) { - return map.containsKey(key); + public V get(final Object key) { + return valueOf(map.get(key)); + } + + /** + * Returns the value for the given key if it exists, or computes it otherwise. + * If a value already exists in the cache, then it is returned immediately. + * Otherwise the {@code creator.call()} method is invoked and its result is saved in this cache for future reuse. + * + * <div class="note"><b>Example:</b> + * the following example shows how this method can be used. + * In particular, it shows how to propagate {@code MyCheckedException}: + * + * {@preformat java + * private final Cache<String,MyObject> cache = new Cache<String,MyObject>(); + * + * public MyObject getMyObject(final String key) throws MyCheckedException { + * try { + * return cache.getOrCreate(key, new Callable<MyObject>() { + * public MyObject call() throws MyCheckedException { + * return createMyObject(key); + * } + * }); + * } catch (MyCheckedException | RuntimeException e) { + * throw e; + * } catch (Exception e) { + * throw new UndeclaredThrowableException(e); + * } + * } + * } + * </div> + * + * This method is similar to {@link #computeIfAbsent(Object, Function)} except that it can propagate + * checked exceptions. If the {@code creator} function does not throw any checked exception, then + * invoking {@code computeIfAbsent(…)} is simpler. + * + * @param key the key for which to get the cached or created value. + * @param creator a method for creating a value, to be invoked only if no value are cached for the given key. + * @return the value for the given key, which may have been created as a result of this method call. + * @throws Exception if an exception occurred during the execution of {@code creator.call()}. + * + * @see #get(Object) + * @see #peek(Object) + * @see #computeIfAbsent(Object, Function) + */ + public V getOrCreate(final K key, final Callable<? extends V> creator) throws Exception { + V value = peek(key); + if (value == null) { + final Handler<V> handler = lock(key); + try { + value = handler.peek(); + if (value == null) { + value = creator.call(); + } + } finally { + handler.putAndUnlock(value); + } + } + return value; + } + + /** + * Returns the value for the given key if it exists, or computes it otherwise. + * If a value already exists in the cache, then it is returned immediately. + * Otherwise the {@code creator.apply(Object)} method is invoked and its result + * is saved in this cache for future reuse. + * + * <div class="note"><b>Example:</b> + * below is the same code than {@link #getOrCreate(Object, Callable)} example, + * but without the need for any checked exception handling: + * + * {@preformat java + * private final Cache<String,MyObject> cache = new Cache<String,MyObject>(); + * + * public MyObject getMyObject(final String key) { + * return cache.computeIfAbsent(key, (k) -> createMyObject(k)); + * } + * } + * </div> + * + * This method is similar to {@link #getOrCreate(Object, Callable)}, but without checked exceptions. + * + * @param key the key for which to get the cached or created value. + * @param creator a method for creating a value, to be invoked only if no value are cached for the given key. + * @return the value already mapped to the key, or the newly computed value. + * + * @since 1.0 + * + * @see #peek(Object) + * @see #containsKey(Object) + * @see #getOrCreate(Object, Callable) + * @see #computeIfPresent(Object, BiFunction) + */ + @Override + public V computeIfAbsent(final K key, final Function<? super K, ? extends V> creator) { + V value = peek(key); + if (value == null) { + final Handler<V> handler = lock(key); + try { + value = handler.peek(); + if (value == null) { + value = creator.apply(key); + } + } finally { + handler.putAndUnlock(value); + } + } + return value; } /** @@ -273,7 +384,19 @@ public class Cache<K,V> extends Abstract } /** + * Ensures that the given value is not an instance of a reserved type. + */ + private static void ensureValidType(final Object value) throws IllegalArgumentException { + if (isReservedType(value)) { + throw new IllegalArgumentException(Errors.format( + Errors.Keys.IllegalArgumentClass_2, "value", value.getClass())); + } + } + + /** * Returns the value of the given object, unwrapping it if possible. + * If the value is under computation in another thread, this method + * will block until the computation is completed. */ @SuppressWarnings("unchecked") private static <V> V valueOf(final Object value) { @@ -291,76 +414,362 @@ public class Cache<K,V> extends Abstract } /** - * Puts the given value in cache. + * Returns the value of the given object if it is not under computation. + * This method is similar to {@link #valueOf(Object)} except that it does + * not block if the value is under computation. + */ + @SuppressWarnings("unchecked") + private static <V> V immediateValueOf(final Object value) { + if (value instanceof Reference<?>) { + return ((Reference<V>) value).get(); + } + if (value instanceof Handler<?>) { + return null; + } + return (V) value; + } + + /** + * Notifies this {@code Cache} instance that an entry has changed. This methods adjusts + * cost calculation. This may cause some strong references to become weak references. + * + * @param key key of the entry that changed. + * @param value the new value. May be {@code null}. + */ + final void notifyChange(final K key, final V value) { + DelayedExecutor.schedule(new Strong(key, value)); + } + + /** + * If no value is already mapped and no value is under computation for the given key, puts the given value + * in the cache. Otherwise returns the current value (potentially blocking until the computation finishes). + * A null {@code value} argument is equivalent to a no-op. Otherwise a {@code null} return value means that + * the given {@code value} has been stored in the {@code Cache}. * - * @param key the key for which to set a value. - * @param value the value to store. - * @return the value previously stored at the given key, or {@code null} if none. + * @param key the key to associate with a value. + * @param value the value to associate with the given key if no value already exists, or {@code null}. + * @return the existing value mapped to the given key, or {@code null} if none existed before this method call. + * + * @see #get(Object) + * @see #computeIfAbsent(Object, Function) + * + * @since 1.0 + */ + @Override + public V putIfAbsent(final K key, final V value) { + if (value == null) { + return null; + } + ensureValidType(value); + final Object previous = map.putIfAbsent(key, value); + if (previous == null) { + // A non-null value means that 'putIfAbsent' did nothing. + notifyChange(key, value); + } + return valueOf(previous); + } + + /** + * Puts the given value in cache and immediately returns the old value. + * A null {@code value} argument removes the entry. If a different value is under computation in another thread, + * then the other thread may fail with an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} + * returns {@code true}. For more safety, consider using {@link #putIfAbsent putIfAbsent(…)} instead. + * + * @param key the key to associate with a value. + * @param value the value to associate with the given key, or {@code null} for removing the mapping. + * @return the value previously mapped to the given key, or {@code null} if no value existed before this + * method call or if the value was under computation in another thread. + * + * @see #get(Object) + * @see #putIfAbsent(Object, Object) */ @Override public V put(final K key, final V value) { - if (isReservedType(value)) { - throw new IllegalArgumentException(Errors.format( - Errors.Keys.IllegalArgumentClass_2, "value", value.getClass())); + ensureValidType(value); + final Object previous = (value != null) ? map.put(key, value) : map.remove(key); + if (previous != value) { + notifyChange(key, value); } - final Object previous; - if (value != null) { - previous = map.put(key, value); - DelayedExecutor.schedule(new Strong(key, value)); + return immediateValueOf(previous); + } + + /** + * If the given key is mapped to any value, replaces that value with the given new value. + * Otherwise does nothing. A null {@code value} argument removes the entry. + * If a different value is under computation in another thread, then the other thread may fail with + * an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} returns {@code true}. + * + * @param key key of the value to replace. + * @param value the new value to use in replacement of the previous one, or {@code null} for removing the mapping. + * @return the value previously mapped to the given key, or {@code null} if no value existed before this + * method call or if the value was under computation in another thread. + * + * @see #replace(Object, Object, Object) + * + * @since 1.0 + */ + @Override + public V replace(final K key, final V value) { + ensureValidType(value); + final Object previous = (value != null) ? map.replace(key, value) : map.remove(key); + if (previous != null) { + // A null value means that 'replace' did nothing. + notifyChange(key, value); + } + return immediateValueOf(previous); + } + + /** + * If the given key is mapped to the given old value, replaces that value with the given new value. + * Otherwise does nothing. A null {@code value} argument removes the entry if the condition matches. + * If a value is under computation in another thread, then this method unconditionally returns {@code false}. + * + * @param key key of the value to replace. + * @param oldValue previous value expected to be mapped to the given key. + * @param newValue the new value to put if the condition matches, or {@code null} for removing the mapping. + * @return {@code true} if the value has been replaced, {@code false} otherwise. + * + * @since 1.0 + */ + @Override + public boolean replace(final K key, final V oldValue, final V newValue) { + ensureValidType(newValue); + final boolean done; + if (oldValue != null) { + done = (newValue != null) ? map.replace(key, oldValue, newValue) : map.remove(key, oldValue); } else { - previous = map.remove(key); + done = (newValue != null) && map.putIfAbsent(key, newValue) == null; } - return Cache.<V>valueOf(previous); + if (done) { + notifyChange(key, newValue); + } + return done; } /** - * Removes the value associated to the given key in the cache. + * Iterates over all entries in the cache and replaces their value with the one provided by the given function. + * If the function throws an exception, the iteration is stopped and the exception is propagated. If any value + * is under computation in other threads, then the iteration will block on that entry until its computation is + * completed. * - * @param key the key of the value to removed. - * @return the value that were associated to the given key, or {@code null} if none. + * @param remapping the function computing new values from the old ones. + * + * @since 1.0 */ @Override - public V remove(final Object key) { - return Cache.<V>valueOf(map.remove(key)); + public void replaceAll(final BiFunction<? super K, ? super V, ? extends V> remapping) { + final ReplaceAdapter adapter = new ReplaceAdapter(remapping); + map.replaceAll(adapter); + Deferred.notifyChanges(this, adapter.changes); } /** - * Returns the value associated to the given key in the cache. This method is similar to - * {@link #peek} except that it blocks if the value is currently under computation in an - * other thread. + * Replaces the value mapped to the given key by a new value computed from the old value. + * If a value for the given key is under computation in another thread, then this method + * blocks until that computation is completed. This is equivalent to the work performed + * by {@link #replaceAll replaceAll(…)} but on a single entry. * - * @param key the key of the value to get. - * @return the value associated to the given key, or {@code null} if none. + * @param key key of the value to replace. + * @param remapping the function computing new values from the old ones. + * @return the new value associated with the given key. + * + * @see #computeIfAbsent(Object, Function) + * + * @since 1.0 */ @Override - public V get(final Object key) { - return Cache.<V>valueOf(map.get(key)); + public V computeIfPresent(final K key, final BiFunction<? super K, ? super V, ? extends V> remapping) { + final ReplaceAdapter adapter = new ReplaceAdapter(remapping); + final Object value = map.computeIfPresent(key, adapter); + Deferred.notifyChanges(this, adapter.changes); + return valueOf(value); } /** - * Returns the value for the given key. If a value already exists in the cache, then it - * is returned immediately. Otherwise the {@code creator.call()} method is invoked and - * its result is saved in this cache for future reuse. + * Replaces the value mapped to the given key by a new value computed from the old value. + * If there is no value for the given key, then the "old value" is taken as {@code null}. + * If a value for the given key is under computation in another thread, then this method + * blocks until that computation is completed. This method is equivalent to + * {@link #computeIfPresent computeIfPresent(…)} except that a new value will be computed + * even if no value existed for the key before this method call. * - * @param key the key for which to get the cached or created value. - * @param creator a method for creating a value, to be invoked only if no value are cached for the given key. - * @return the value for the given key, which may have been created as a result of this method call. - * @throws Exception if an exception occurred during the execution of {@code creator.call()}. + * @param key key of the value to replace. + * @param remapping the function computing new values from the old ones, or from a {@code null} value. + * @return the new value associated with the given key. + * + * @see #computeIfAbsent(Object, Function) + * + * @since 1.0 */ - public V getOrCreate(final K key, final Callable<? extends V> creator) throws Exception { - V value = peek(key); - if (value == null) { - final Handler<V> handler = lock(key); - try { - value = handler.peek(); - if (value == null) { - value = creator.call(); + @Override + public V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> remapping) { + final ReplaceAdapter adapter = new ReplaceAdapter(remapping); + final Object value = map.compute(key, adapter); + Deferred.notifyChanges(this, adapter.changes); + return valueOf(value); + } + + /** + * Maps the given value to the given key if no mapping existed before this method call, + * or computes a new value otherwise. If a value for the given key is under computation + * in another thread, then this method blocks until that computation is completed. + * + * @param key key of the value to replace. + * @param value the value to associate with the given key if no value already exists, or {@code null}. + * @param remapping the function computing a new value by merging the exiting value + * with the {@code value} argument given to this method. + * @return the new value associated with the given key. + * + * @since 1.0 + */ + @Override + public V merge(final K key, final V value, final BiFunction<? super V, ? super V, ? extends V> remapping) { + ensureValidType(value); + + /** Similar to {@link Cache.ReplaceAdapter}, but adapted to the merge case. */ + final class Adapter implements BiFunction<Object,Object,Object> { + /** Forwards {@link Cache#map} calls to the user-provided function. */ + @Override public Object apply(final Object oldValue, final Object givenValue) { + final V toReplace = valueOf(oldValue); + final V newValue = remapping.apply(toReplace, valueOf(givenValue)); + ensureValidType(newValue); + if (newValue != toReplace) { + changes = new Deferred<>(key, newValue, changes); } - } finally { - handler.putAndUnlock(value); + return newValue; } + + /** The new values for which to send notifications. */ + Deferred<K,V> changes; } - return value; + final Adapter adapter = new Adapter(); + final Object newValue = map.merge(key, value, adapter); + Deferred.notifyChanges(this, adapter.changes); + return valueOf(newValue); + } + + /** + * A callback for {@link Cache#map} which forwards the calls to the {@code remapping} function provided by user. + * Before to forward the calls, {@code ReplaceAdapter} verifies if the value is under computation. If yes, then + * this adapter block until the value is available for forwarding it to the user. + */ + private final class ReplaceAdapter implements BiFunction<K,Object,Object> { + /** The new values for which to send notifications. */ + private Deferred<K,V> changes; + + /** The user-providing function. */ + private final BiFunction<? super K, ? super V, ? extends V> remapping; + + /** Creates a new adapter for the given user-provided function. */ + ReplaceAdapter(final BiFunction<? super K, ? super V, ? extends V> remapping) { + this.remapping = remapping; + } + + /** Forwards {@link Cache#map} calls to the user-provided function. */ + @Override public Object apply(final K key, final Object oldValue) { + final V toReplace = valueOf(oldValue); + final V newValue = remapping.apply(key, toReplace); + ensureValidType(newValue); + if (newValue != toReplace) { + changes = new Deferred<>(key, newValue, changes); + } + return newValue; + } + } + + /** + * Key-value pairs of new entries created during {@link Cache.ReplaceAdapter} execution, as a chained list. + * Calls to {@link Cache#notifyChange(Object, Object)} for those entries need to be deferred until operation + * on {@link Cache#map} completed because {@link Cache#adjustReferences(Object, Object)} needs the new values + * to be present in the map. + */ + private static final class Deferred<K,V> { + private final K key; + private final V value; + private final Deferred<K,V> next; + + /** Creates a new notification to be sent after the {@link Cache#map} operation completed. */ + Deferred(final K key, final V value, final Deferred<K,V> next) { + this.key = key; + this.value = value; + this.next = next; + } + + /** Sends all deferred notifications, starting with the given one. */ + static <K,V> void notifyChanges(final Cache<K,V> cache, Deferred<K,V> entry) { + while (entry != null) { + cache.notifyChange(entry.key, entry.value); + entry = entry.next; + } + } + } + + /** + * Removes the value mapped to the given key in the cache. If a value is under computation in another thread, + * then the other thread may fail with an {@link IllegalStateException} unless {@link #isKeyCollisionAllowed()} + * returns {@code true}. For more safety, consider using {@link #remove(Object, Object)} instead. + * + * @param key the key of the value to removed. + * @return the value previously mapped to the given key, or {@code null} if no value existed before this + * method call or if the value was under computation in another thread. + * + * @see #get(Object) + * @see #remove(Object, Object) + */ + @Override + @SuppressWarnings("unchecked") + public V remove(final Object key) { + final Object oldValue = map.remove(key); + if (oldValue != null) { + notifyChange((K) key, null); + } + return immediateValueOf(oldValue); + } + + /** + * If the given key is mapped to the given old value, removes that value. Otherwise does nothing. + * If a value is under computation in another thread, then this method unconditionally returns {@code false}. + * + * @param key key of the value to remove. + * @param oldValue previous value expected to be mapped to the given key. + * @return {@code true} if the value has been removed, {@code false} otherwise. + * + * @see #get(Object) + * + * @since 1.0 + */ + @Override + @SuppressWarnings("unchecked") + public boolean remove(final Object key, final Object oldValue) { + final boolean done = map.remove(key, oldValue); + if (done) { + notifyChange((K) key, null); + } + return done; + } + + /** + * Returns {@code true} if this map contains the specified key. + * If the value is under computation in another thread, this method returns {@code true} + * without waiting for the computation result. This behavior is consistent with other + * {@code Map} methods in the following ways: + * + * <ul> + * <li>{@link #get(Object)} blocks until the computation is completed.</li> + * <li>{@link #put(Object, Object)} returns {@code null} for values under computation, + * i.e. behaves as if keys are temporarily mapped to the {@code null} value until + * the computation is completed.</li> + * </ul> + * + * @param key the key to check for existence. + * @return {@code true} if the given key is mapped to an existing value or a value under computation. + * + * @see #get(Object) + * @see #peek(Object) + */ + @Override + public boolean containsKey(final Object key) { + return map.containsKey(key); } /** @@ -370,6 +779,9 @@ public class Cache<K,V> extends Abstract * * @param key the key for which to get the cached value. * @return the cached value for the given key, or {@code null} if there is none. + * + * @see #get(Object) + * @see #lock(Object) */ public V peek(final K key) { final Object value = map.get(key); @@ -386,7 +798,7 @@ public class Cache<K,V> extends Abstract final V result = ref.get(); if (result != null && map.replace(key, ref, result)) { ref.clear(); // Prevents the reference from being enqueued. - DelayedExecutor.schedule(new Strong(key, result)); + notifyChange(key, result); } return result; } @@ -397,9 +809,9 @@ public class Cache<K,V> extends Abstract /** * Invoked from the a background thread after a {@linkplain WeakReference weak} - * or {@linkplain SoftReference soft} reference has been replaced by a strong one. It will - * looks for older strong references to replace by weak references so that the total cost - * stay below the cost limit. + * or {@linkplain SoftReference soft} reference has been replaced by a strong one. + * It will looks for older strong references to replace by weak references so that + * the total cost stay below the cost limit. */ private final class Strong extends DelayedRunnable.Immediate { private final K key; @@ -479,7 +891,7 @@ public class Cache<K,V> extends Abstract */ if (map.replace(key, ref, result)) { ref.clear(); // Prevents the reference from being enqueued. - DelayedExecutor.schedule(new Strong(key, result)); + notifyChange(key, result); } return new Simple<>(result); } @@ -530,7 +942,7 @@ public class Cache<K,V> extends Abstract return work.new Wait(); } /* - * A calculation has already been completed. Returns a wrapper + * A computation has already been completed. Returns a wrapper * which will just return the result without any processing. */ assert !isReservedType(value) : value; @@ -540,9 +952,8 @@ public class Cache<K,V> extends Abstract } /** - * The handler returned by {@link Cache#lock}, to be used for unlocking and storing the - * result. This handler should be used as below (note the {@code try} … {@code catch} - * blocks, which are <strong>mandatory</strong>): + * The handler returned by {@link Cache#lock}, to be used for unlocking and storing the result. + * This handler should be used as below (the {@code try} … {@code finally} statements are important): * * {@preformat java * Value V = null; @@ -593,12 +1004,12 @@ public class Cache<K,V> extends Abstract /** * A simple handler implementation wrapping an existing value. This implementation - * is used when the value has been fully calculated in an other thread before this + * is used when the value has been fully computed in an other thread before this * thread could start its work. */ private final class Simple<V> implements Handler<V> { /** - * The result calculated in an other thread. + * The result computed in an other thread. */ private final V value; @@ -610,7 +1021,7 @@ public class Cache<K,V> extends Abstract } /** - * Returns the calculated value. + * Returns the computed value. */ @Override public V peek() { @@ -663,8 +1074,8 @@ public class Cache<K,V> extends Abstract } /** - * Waits for the completion of the value computation and returns this result. This - * method should be invoked only from an other thread than the one doing the calculation. + * Waits for the completion of the value computation and returns this result. + * This method should be invoked only from another thread than the one doing the computation. */ @Override public V get() { @@ -713,6 +1124,8 @@ public class Cache<K,V> extends Abstract } if (done) { DelayedExecutor.schedule(this); + } else if (!isKeyCollisionAllowed()) { + throw new IllegalStateException(Errors.format(Errors.Keys.KeyCollision_1, key)); } } @@ -766,8 +1179,8 @@ public class Cache<K,V> extends Abstract * than the cost limit, then oldest strong references are replaced by weak references. */ final void adjustReferences(final K key, final V value) { - int cost = cost(value); - synchronized (costs) { // Should not be needed, but done as a safety. + int cost = (value != null) ? cost(value) : 0; + synchronized (costs) { final Integer old = costs.put(key, cost); if (old != null) { cost -= old; @@ -779,7 +1192,7 @@ public class Cache<K,V> extends Abstract * Converts the current entry from strong reference to weak/soft reference. * We perform this conversion even if the entry is for the value just added * to the cache, if it happen that the cost is higher than the maximal one. - * That entry should not be garbage collected to early anyway because the + * That entry should not be garbage collected too early anyway because the * caller should still have a strong reference to the value he just created. */ final Map.Entry<K,Integer> entry = it.next(); Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -532,7 +532,7 @@ public class DefaultTreeTable implements * cast will need to be replaced by an "instanceof" check. */ @Override - @SuppressWarnings("ReturnOfCollectionOrArrayField") // Returned list is modifiable on intend. + @SuppressWarnings("ReturnOfCollectionOrArrayField") // Returned list is modifiable on intent. public final List<TreeTable.Node> getChildren() { if (children == null) { if (isLeaf()) { Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/FrequencySortedSet.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/FrequencySortedSet.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/FrequencySortedSet.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/FrequencySortedSet.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -67,7 +67,7 @@ public class FrequencySortedSet<E> exten * {@code 0} if the element should be sorted in the usual order, or {@code -1} * if the elements should be sorted in reverse order (most frequent element first). * This value is XORed with the number of times <var>n</var> that an element is added: {@code n ^ order}. - * The intend is to store negative numbers in the {@link #count} map if this {@code FrequencySortedSet} + * The intent is to store negative numbers in the {@link #count} map if this {@code FrequencySortedSet} * has been created for reverse order. * * <div class="note"><b>Implementation note:</b> Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -19,22 +19,20 @@ package org.apache.sis.util.collection; import java.util.Arrays; import java.util.AbstractList; import java.util.RandomAccess; +import java.util.Spliterator; +import java.util.PrimitiveIterator; import java.util.NoSuchElementException; import java.util.ConcurrentModificationException; +import java.util.function.Consumer; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; import java.io.IOException; import java.io.Serializable; import java.io.ObjectOutputStream; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.ArgumentChecks; -// Branch-dependent imports -import java.util.Spliterator; -import java.util.PrimitiveIterator; -import java.util.function.Consumer; -import java.util.function.IntConsumer; -import java.util.stream.IntStream; -import java.util.stream.StreamSupport; - /** * A list of unsigned integer values. This class packs the values in the minimal amount of bits Modified: sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java?rev=1825252&r1=1825251&r2=1825252&view=diff ============================================================================== --- sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java [UTF-8] (original) +++ sis/branches/JDK9/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java [UTF-8] Sat Feb 24 15:44:08 2018 @@ -349,7 +349,7 @@ public class DefaultNameSpace implements * Returns a child namespace of the given name. The returned namespace will * have this namespace as its parent, and will use the same separator. * - * <p>The {@link #headSeparator} is not inherited by the children on intend, because this + * <p>The {@link #headSeparator} is not inherited by the children on intent, because this * method is used only by {@link DefaultScopedName} constructors in order to create a * sequence of parsed local names. For example in {@code "http://www.opengeospatial.org"} * the head separator is {@code "://"} for {@code "www"} (which is having this namespace),
