You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2018/01/25 19:23:43 UTC
svn commit: r1822222 - in /sis/trunk: ./ application/sis-openoffice/
core/sis-build-helper/src/main/java/org/apache/sis/internal/book/
core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/
core/sis-metadata/src/main/java/org/apache/sis...
Author: desruisseaux
Date: Thu Jan 25 19:23:43 2018
New Revision: 1822222
URL: http://svn.apache.org/viewvc?rev=1822222&view=rev
Log:
Last merge from JDK7 branch.
Modified:
sis/trunk/ (props changed)
sis/trunk/application/sis-openoffice/pom.xml
sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/Assembler.java
sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/CodeColorizer.java
sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/GEOAPI.lst
sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/OGC.lst
sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/SIS.lst
sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java
sis/trunk/pom.xml
Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Jan 25 19:23:43 2018
@@ -1,5 +1,5 @@
/sis/branches/Android:1430670-1480699
/sis/branches/JDK6:1394364-1758914
-/sis/branches/JDK7:1394913-1815605
-/sis/branches/JDK8:1584960-1815604
+/sis/branches/JDK7:1394913-1822221
+/sis/branches/JDK8:1584960-1822220
/sis/branches/JDK9:1773327-1803064
Modified: sis/trunk/application/sis-openoffice/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/application/sis-openoffice/pom.xml?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/application/sis-openoffice/pom.xml (original)
+++ sis/trunk/application/sis-openoffice/pom.xml Thu Jan 25 19:23:43 2018
@@ -141,7 +141,7 @@
<dependency>
<groupId>org.apache.sis.non-free</groupId>
<artifactId>sis-embedded-data</artifactId>
- <version>0.8-SNAPSHOT</version>
+ <version>${sis.non-free.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/Assembler.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/Assembler.java?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/Assembler.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/Assembler.java [UTF-8] Thu Jan 25 19:23:43 2018
@@ -391,7 +391,9 @@ public final class Assembler {
tableOfChapterContent = document.createElement("ul");
tableOfChapterContent.setAttribute("class", "toc");
final Node nav = document.createElement("nav");
- nav.appendChild(document.createTextNode(resources.getString("this-chapter")));
+ final Node p = document.createElement("p");
+ p.appendChild(document.createTextNode(resources.getString("this-chapter")));
+ nav.appendChild(p);
nav.appendChild(tableOfChapterContent);
Node insertionPoint = node.getParentNode(); // The <header> element.
do insertionPoint = insertionPoint.getNextSibling(); // The first paragraph.
Modified: sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/CodeColorizer.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/CodeColorizer.java?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/CodeColorizer.java [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/java/org/apache/sis/internal/book/CodeColorizer.java [UTF-8] Thu Jan 25 19:23:43 2018
@@ -182,6 +182,17 @@ public final class CodeColorizer {
}
break;
}
+ default: {
+ int upper = word.length();
+ while (upper > 0) {
+ final int c = word.codePointBefore(upper);
+ if (Character.isUnicodeIdentifierPart(c)) {
+ word = word.substring(0, upper);
+ break;
+ }
+ upper -= Character.charCount(c);
+ }
+ }
}
/*
* Check if the keyword is a known one. The 'identifierOrigins' map contains only simple name
@@ -322,7 +333,9 @@ public final class CodeColorizer {
nextSubstringStart = lower;
lower = text.indexOf(stopCondition, lower+1);
if (lower >= 0) {
- lower += stopCondition.length();
+ if (!stopCondition.equals("\n")) {
+ lower += stopCondition.length();
+ }
stopCondition = null;
} else {
lower = text.length();
Modified: sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/GEOAPI.lst
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/GEOAPI.lst?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/GEOAPI.lst [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/GEOAPI.lst [UTF-8] Thu Jan 25 19:23:43 2018
@@ -64,6 +64,7 @@ Ellipsoid
EllipsoidalCS
EngineeringCRS
EngineeringDatum
+Envelope
EnvironmentalRecord
EvaluationMethodType
Event
@@ -233,3 +234,4 @@ VerticalCS
VerticalDatum
VerticalDatumType
VerticalExtent
+createGeodeticDatum
Modified: sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/OGC.lst
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/OGC.lst?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/OGC.lst [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/OGC.lst [UTF-8] Thu Jan 25 19:23:43 2018
@@ -233,4 +233,6 @@ SV_OperationChainMetadata
SV_OperationMetadata
SV_ParameterDirection
SV_ServiceIdentification
+AXIS
+ORDER
TOWGS84
Modified: sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/SIS.lst
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/SIS.lst?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/SIS.lst [UTF-8] (original)
+++ sis/trunk/core/sis-build-helper/src/main/resources/org/apache/sis/internal/book/SIS.lst [UTF-8] Thu Jan 25 19:23:43 2018
@@ -1,16 +1,37 @@
+AbstractCRS
AbstractMathTransform
AngleFormat
+AxesConvention
+Citations
+CommonCRS
DataStore
+DataStores
DefaultEllipsoid
+DefaultExtent
DefaultGeodeticDatum
DefaultMetadata
DefaultProjectedCRS
+DirectPosition2D
+Envelope2D
Envelopes
+Extents
GeneralEnvelope
GridGeometry
IdentifierSpace
+KeyNamePolicy
Localized
+MathTransforms
+Matrices
+MetadataStandard
NilObject
NilReason
NumberRange
+ObjectConverter
Range
+ValueExistencePolicy
+JAVABEANS_PROPERTY
+NON_EMPTY
+RIGHT_HANDED
+asValueMap
+castOrCopy
+forConvention
Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java [UTF-8] Thu Jan 25 19:23:43 2018
@@ -61,11 +61,11 @@ import org.apache.sis.internal.jdk8.JDK8
* This class provides methods for:
*
* <ul>
- * <li>{@link #getGeographicBoundingBox(Extent)}, {@link #getVerticalRange(Extent)}
- * and {@link #getDate(Extent, double)}
- * for fetching geographic or temporal components in a convenient form.</li>
- * <li>Methods for computing {@linkplain #intersection intersection} of bounding boxes
- * and {@linkplain #area area} estimations.</li>
+ * <li>{@linkplain #getGeographicBoundingBox Fetching geographic},
+ * {@linkplain #getVerticalRange vertical} or
+ * {@linkplain #getDate temporal components} in a convenient form.</li>
+ * <li>Computing {@linkplain #intersection intersection} of bounding boxes</li>
+ * <li>Computing {@linkplain #area area} estimations.</li>
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
Modified: sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java [UTF-8] Thu Jan 25 19:23:43 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/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] (original)
+++ sis/trunk/core/sis-utility/src/main/java/org/apache/sis/util/collection/Cache.java [UTF-8] Thu Jan 25 19:23:43 2018
@@ -40,47 +40,49 @@ import org.apache.sis.internal.jdk8.Supp
/**
- * 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:
*
- * <ol>
- * <li>Check if the value is already available in the map.
- * If it is, return it immediately and we are done.</li>
- * <li>Otherwise, get a lock and check again if the value is already available in the map
- * (because the value could have been computed by an other thread between step 1 and
- * the obtention of the lock). If it is, release the lock and we are done.</li>
- * <li>Otherwise compute the value, store the result and release the lock.</li>
- * </ol>
+ * <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 (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:
+ * This class can be used by invoking
+ * or {@link #getOrCreate getOrCreate(…)} with lambda functions as below:
*
* {@preformat java
* private final Cache<String,MyObject> cache = new Cache<String,MyObject>();
*
- * public MyObject getMyObject(final String key) throws MyCheckedException {
+ * public MyObject getMyObject(String key) {
* try {
- * return cache.getOrCreate(key, new Callable<MyObject>() {
- * public MyObject call() throws MyCheckedException {
- * return createMyObject(key);
- * }
- * });
- * } catch (MyCheckedException | RuntimeException e) {
- * throw e;
+ * return cache.getOrCreate(key, (k) -> createMyObject(k));
* } catch (Exception e) {
- * throw new UndeclaredThrowableException(e);
+ * // Decide here what to rethrow.
* }
* }
* }
*
- * 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}).
+ * 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.
+ * If it is, return it immediately and we are done.</li>
+ * <li>Otherwise, get a lock and check again if the value is already available in the map
+ * (because the value could have been computed by an other thread between step 1 and
+ * the obtention of the lock). If it is, release the lock and we are done.</li>
+ * <li>Otherwise compute the value, store the result and release the lock.</li>
+ * </ol>
+ *
+ * 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 +132,8 @@ import org.apache.sis.internal.jdk8.Supp
* <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 +141,11 @@ import org.apache.sis.internal.jdk8.Supp
* @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 +227,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 +257,77 @@ 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;
}
/**
@@ -273,7 +339,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 +369,231 @@ 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 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 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 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;
+ }
+ if (done) {
+ notifyChange(key, newValue);
+ }
+ return done;
+ }
+
+ /**
+ * 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;
+ }
}
- return Cache.<V>valueOf(previous);
}
/**
- * Removes the value associated to the given key in the cache.
+ * 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 that were associated to the given key, or {@code null} if none.
+ * @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) {
- return Cache.<V>valueOf(map.remove(key));
+ final Object oldValue = map.remove(key);
+ if (oldValue != null) {
+ notifyChange((K) key, null);
+ }
+ return immediateValueOf(oldValue);
}
/**
- * 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.
+ * 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 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 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
- public V get(final Object key) {
- return Cache.<V>valueOf(map.get(key));
+ @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 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.
+ * 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 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 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)
*/
- 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;
+ @Override
+ public boolean containsKey(final Object key) {
+ return map.containsKey(key);
}
/**
@@ -370,6 +603,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 +622,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 +633,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 +715,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 +766,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 +776,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 +828,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 +845,7 @@ public class Cache<K,V> extends Abstract
}
/**
- * Returns the calculated value.
+ * Returns the computed value.
*/
@Override
public V peek() {
@@ -663,8 +898,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 +948,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 +1003,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 +1016,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/trunk/pom.xml
URL: http://svn.apache.org/viewvc/sis/trunk/pom.xml?rev=1822222&r1=1822221&r2=1822222&view=diff
==============================================================================
--- sis/trunk/pom.xml (original)
+++ sis/trunk/pom.xml Thu Jan 25 19:23:43 2018
@@ -499,6 +499,7 @@ Apache SIS is a free software, Java lang
<maven.compile.source>1.7</maven.compile.source>
<maven.compile.target>1.7</maven.compile.target>
<sis.plugin.version>${project.version}</sis.plugin.version>
+ <sis.non-free.version>0.8</sis.non-free.version>
<geoapi.version>3.0.1</geoapi.version>
</properties>