Author: desruisseaux
Date: Wed Oct 7 13:30:49 2015
New Revision: 1707299
URL: http://svn.apache.org/viewvc?rev=1707299&view=rev
Log:
Merge from the JDK7 branch.
Added:
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java
- copied, changed from r1707296, sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMap.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java
- copied, changed from r1707296, sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/ModifiableIdentifierMapTest.java
Removed:
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCases.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapWithSpecialCasesTest.java
Modified:
sis/branches/JDK6/ (props changed)
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFractionTest.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java
sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/NonMarshalledAuthority.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/gco/PropertyType.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jaxb/package-info.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/collection/CodeListSet.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/collection/Containers.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/IdentifierMap.java
sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/NilObjectHandler.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/IdentifierMapAdapterTest.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/jaxb/gco/PropertyTypeMock.java
sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Oct 7 13:30:49 2015
@@ -1,4 +1,4 @@
/sis/branches/Android:1430670-1480699
-/sis/branches/JDK7:1394913-1706794
-/sis/branches/JDK8:1584960-1706793
+/sis/branches/JDK7:1394913-1707296
+/sis/branches/JDK8:1584960-1707295
/sis/trunk:1394364-1508466,1519089-1519674
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -18,12 +18,15 @@ package org.apache.sis.metadata;
import java.util.Map;
import java.util.Set;
+import java.util.EnumSet;
import java.util.Iterator;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.collection.CodeListSet;
import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.metadata.iso.identification.DefaultRepresentativeFraction;
/**
@@ -33,7 +36,7 @@ import org.apache.sis.internal.util.Coll
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
final class Cloner extends org.apache.sis.internal.util.Cloner {
@@ -53,6 +56,15 @@ final class Cloner extends org.apache.si
}
/**
+ * Recursively clones all elements in the given array.
+ */
+ private void clones(final Object[] array) throws CloneNotSupportedException {
+ for (int i=0; i<array.length; i++) {
+ array[i] = clone(array[i]);
+ }
+ }
+
+ /**
* Returns an unmodifiable copy of the specified object.
* This method performs the following heuristic tests:
*
@@ -73,12 +85,17 @@ final class Cloner extends org.apache.si
@Override
public Object clone(final Object object) throws CloneNotSupportedException {
/*
- * CASE 1 - The object is an implementation of ModifiableMetadata. It may have
+ * CASE 1 - The object is an org.apache.sis.metadata.* implementation. It may have
* its own algorithm for creating an unmodifiable view of metadata.
*/
if (object instanceof ModifiableMetadata) {
return ((ModifiableMetadata) object).unmodifiable();
}
+ if (object instanceof DefaultRepresentativeFraction) {
+ final DefaultRepresentativeFraction c = ((DefaultRepresentativeFraction) object).clone();
+ c.freeze();
+ return c;
+ }
/*
* CASE 2 - The object is a collection. All elements are replaced by their
* unmodifiable variant and stored in a new collection of similar
@@ -87,29 +104,39 @@ final class Cloner extends org.apache.si
if (object instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) object;
final boolean isSet = (collection instanceof Set<?>);
- if (collection.isEmpty()) {
- if (isSet) {
- collection = Collections.EMPTY_SET;
- } else {
- collection = Collections.EMPTY_LIST;
+ final Object[] array = collection.toArray();
+ switch (array.length) {
+ case 0: {
+ collection = isSet ? Collections.EMPTY_SET
+ : Collections.EMPTY_LIST;
+ break;
}
- } else {
- final Object[] array = collection.toArray();
- for (int i=0; i<array.length; i++) {
- array[i] = clone(array[i]);
+ case 1: {
+ final Object value = clone(array[0]);
+ collection = isSet ? Collections.singleton(value)
+ : Collections.singletonList(value);
+ break;
}
- // Do not use the SIS Checked* classes since
- // we don't need type checking anymore.
- if (isSet) {
- collection = CollectionsExt.immutableSet(false, array);
- } else {
- // Conservatively assumes a List if we are not sure to have a Set,
- // since the list is less destructive (no removal of duplicated).
- switch (array.length) {
- case 0: collection = Collections.EMPTY_LIST; break; // Redundant with isEmpty(), but we are paranoiac.
- case 1: collection = Collections.singletonList(array[0]); break;
- default: collection = Containers.unmodifiableList(array); break;
+ default: {
+ if (isSet) {
+ if (collection instanceof EnumSet<?>) {
+ collection = Collections.unmodifiableSet(((EnumSet<?>) collection).clone());
+ } else if (collection instanceof CodeListSet<?>) {
+ collection = Collections.unmodifiableSet(((CodeListSet<?>) collection).clone());
+ } else {
+ clones(array);
+ collection = CollectionsExt.immutableSet(false, array);
+ }
+ } else {
+ /*
+ * Do not use the SIS Checked* classes since we don't need type checking anymore.
+ * Conservatively assumes a List if we are not sure to have a Set since the list
+ * is less destructive (no removal of duplicated values).
+ */
+ clones(array);
+ collection = Containers.unmodifiableList(array);
}
+ break;
}
}
return collection;
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -79,8 +79,7 @@ import static org.apache.sis.util.collec
* }
*
* An initially modifiable metadata may become unmodifiable at a later stage
- * (typically after its construction is completed) by the call to the
- * {@link #freeze()} method.
+ * (typically after its construction is completed) by the call to the {@link #freeze()} method.
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
@@ -105,8 +104,7 @@ public abstract class ModifiableMetadata
}
/**
- * A flag used for {@link #unmodifiable} in order to specify that
- * {@link #freeze()} is under way.
+ * A sentinel value used for {@link #unmodifiable} in order to specify that {@link #freeze()} is under way.
*/
private static final ModifiableMetadata FREEZING = new Null();
@@ -129,17 +127,13 @@ public abstract class ModifiableMetadata
* Returns {@code true} if this metadata is modifiable. This method returns
* {@code false} if {@link #freeze()} has been invoked on this object.
*
- * <div class="warning"><b>Warning:</b>
- * this method is likely to change. Future SIS version will probably uses a "state" enumeration
- * (modifiable, appendable, etc.).</div>
- *
* @return {@code true} if this metadata is modifiable.
*
* @see #freeze()
* @see #checkWritePermission()
*/
public final boolean isModifiable() {
- return unmodifiable != this;
+ return unmodifiable != this && unmodifiable != FREEZING;
}
/**
@@ -164,16 +158,11 @@ public abstract class ModifiableMetadata
* The default implementation makes the following choice:
*
* <ul>
- * <li>If this metadata is itself unmodifiable, then this method returns {@code this}
- * unchanged.</li>
+ * <li>If this metadata is itself unmodifiable, then this method returns {@code this} unchanged.</li>
* <li>Otherwise this method {@linkplain #clone() clone} this metadata and
* {@linkplain #freeze() freeze} the clone before to return it.</li>
* </ul>
*
- * <div class="warning"><b>Warning:</b>
- * this method is likely to change. Future SIS version will probably uses a "state" enumeration
- * (modifiable, appendable, etc.).</div>
- *
* @return An unmodifiable copy of this metadata.
*/
public AbstractMetadata unmodifiable() {
@@ -209,13 +198,9 @@ public abstract class ModifiableMetadata
* property after this method call will throw an {@link UnmodifiableMetadataException}.
* If this metadata is already unmodifiable, then this method does nothing.
*
- * <p>Subclasses usually don't need to override this method since the default implementation
+ * <p>Subclasses usually do not need to override this method since the default implementation
* performs its work using Java reflection.</p>
*
- * <div class="warning"><b>Warning:</b>
- * this method is likely to change. Future SIS version will probably uses a "state" enumeration
- * (modifiable, appendable, etc.).</div>
- *
* @see #isModifiable()
* @see #checkWritePermission()
*/
@@ -254,10 +239,13 @@ public abstract class ModifiableMetadata
* @see #freeze()
*/
protected void checkWritePermission() throws UnmodifiableMetadataException {
- if (!isModifiable()) {
- throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ if (unmodifiable != null) {
+ if (unmodifiable == this) {
+ throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ } else if (unmodifiable != FREEZING) {
+ unmodifiable = null;
+ }
}
- unmodifiable = null;
}
/**
@@ -265,8 +253,7 @@ public abstract class ModifiableMetadata
* creating it if needed. This method performs the following steps:
*
* <ul>
- * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
- * modifiable.</li>
+ * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is modifiable.</li>
* <li>If {@code source} is null or empty, returns {@code null}
* (meaning that the metadata property is not provided).</li>
* <li>If {@code target} is null, creates a new {@link List}.</li>
@@ -313,8 +300,7 @@ public abstract class ModifiableMetadata
* creating it if needed. This method performs the following steps:
*
* <ul>
- * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
- * modifiable.</li>
+ * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is modifiable.</li>
* <li>If {@code source} is null or empty, returns {@code null}
* (meaning that the metadata property is not provided).</li>
* <li>If {@code target} is null, creates a new {@link Set}.</li>
@@ -325,8 +311,8 @@ public abstract class ModifiableMetadata
* @param source The source set, or {@code null}.
* @param target The target set, or {@code null} if not yet created.
* @param elementType The base type of elements to put in the set.
- * @return A set (possibly the {@code target} instance) containing the {@code source}
- * elements, or {@code null} if the source was null.
+ * @return A set (possibly the {@code target} instance) containing the {@code source} elements,
+ * or {@code null} if the source was null.
* @throws UnmodifiableMetadataException if this metadata is unmodifiable.
*
* @see #nonNullSet(Set, Class)
@@ -361,8 +347,7 @@ public abstract class ModifiableMetadata
* creating it if needed. This method performs the following steps:
*
* <ul>
- * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is
- * modifiable.</li>
+ * <li>Invokes {@link #checkWritePermission()} in order to ensure that this metadata is modifiable.</li>
* <li>If {@code source} is null or empty, returns {@code null}
* (meaning that the metadata property is not provided).</li>
* <li>If {@code target} is null, creates a new {@link Set} or a new {@link List}
@@ -381,8 +366,8 @@ public abstract class ModifiableMetadata
* @param source The source collection, or {@code null}.
* @param target The target collection, or {@code null} if not yet created.
* @param elementType The base type of elements to put in the collection.
- * @return A collection (possibly the {@code target} instance) containing the {@code source}
- * elements, or {@code null} if the source was null.
+ * @return A collection (possibly the {@code target} instance) containing the {@code source} elements,
+ * or {@code null} if the source was null.
* @throws UnmodifiableMetadataException if this metadata is unmodifiable.
*/
@SuppressWarnings("unchecked")
@@ -671,12 +656,12 @@ public abstract class ModifiableMetadata
*/
@SuppressWarnings({"rawtypes","unchecked"})
protected <E> Class<? extends Collection<E>> collectionType(final Class<E> elementType) {
- return (Class) (CodeList.class.isAssignableFrom(elementType) ||
- Enum.class.isAssignableFrom(elementType) ||
- Charset.class.isAssignableFrom(elementType) ||
- String.class == elementType ||
- Locale.class == elementType ||
- Currency.class == elementType
+ return (Class) (CodeList.class.isAssignableFrom(elementType)
+ || Enum.class.isAssignableFrom(elementType)
+ || Charset.class.isAssignableFrom(elementType)
+ || String.class == elementType
+ || Locale.class == elementType
+ || Currency.class == elementType
? Set.class : List.class);
}
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -29,7 +29,8 @@ import org.apache.sis.xml.IdentifierSpac
import org.apache.sis.xml.IdentifiedObject;
import org.apache.sis.metadata.MetadataStandard;
import org.apache.sis.metadata.ModifiableMetadata;
-import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.internal.jaxb.IdentifierMapAdapter;
+import org.apache.sis.internal.jaxb.ModifiableIdentifierMap;
import org.apache.sis.internal.util.Utilities;
import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.CharSequences;
@@ -52,7 +53,7 @@ import static org.apache.sis.util.collec
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
@XmlTransient
@@ -151,13 +152,14 @@ public class ISOMetadata extends Modifia
*/
identifiers = nonNullCollection(identifiers, Identifier.class);
if (identifiers == null) {
- return IdentifierMapWithSpecialCases.EMPTY;
+ return IdentifierMapAdapter.EMPTY;
}
/*
* We do not cache (for now) the IdentifierMap because it is cheap to create, and if we were
* caching it we would need anyway to check if 'identifiers' still references the same list.
*/
- return new IdentifierMapWithSpecialCases(identifiers);
+ return isModifiable() ? new ModifiableIdentifierMap(identifiers)
+ : new IdentifierMapAdapter(identifiers);
}
@@ -204,7 +206,7 @@ public class ISOMetadata extends Modifia
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
private String getUUID() {
/*
- * IdentifierMapWithSpecialCases will take care of converting UUID to String,
+ * IdentifierMapAdapter will take care of converting UUID to String,
* or to return a previously stored String if it was an unparsable UUID.
*/
return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(IdentifierSpace.UUID);
@@ -216,9 +218,9 @@ public class ISOMetadata extends Modifia
*/
private void setUUID(final String id) {
/*
- * IdentifierMapWithSpecialCases will take care of converting the String to UUID if possible,
- * or will store the value as a plain String if it can not be converted. In the later case, a
- * warning will be emitted (logged or processed by listeners).
+ * IdentifierMapAdapter will take care of converting the String to UUID if possible, or
+ * will store the value as a plain String if it can not be converted. In the later case,
+ * a warning will be emitted (logged or processed by listeners).
*/
getIdentifierMap().put(IdentifierSpace.UUID, id);
}
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/acquisition/DefaultObjective.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -48,7 +48,7 @@ import org.apache.sis.internal.jaxb.NonM
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
@XmlType(name = "MI_Objective_Type", propOrder = {
@@ -175,23 +175,20 @@ public class DefaultObjective extends IS
@Override
@XmlElement(name = "identifier", required = true)
public Collection<Identifier> getIdentifiers() {
- return NonMarshalledAuthority.excludeOnMarshalling(super.getIdentifiers());
+ return NonMarshalledAuthority.filterOnMarshalling(super.getIdentifiers());
}
/**
* Sets the code used to identify the objective.
*
- * <p>This method overwrites all previous identifiers with the given new values,
- * <strong>except</strong> the XML identifiers ({@linkplain IdentifierSpace#ID ID},
- * {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>), if any. We do not overwrite
- * the XML identifiers because they are usually associated to object identity.</p>
+ * <p>XML identifiers ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>),
+ * are not affected by this method, unless they are explicitely provided in the given collection.</p>
*
* @param newValues The new identifiers values.
*/
- public void setIdentifiers(final Collection<? extends Identifier> newValues) {
- final Collection<Identifier> oldIds = NonMarshalledAuthority.filteredCopy(identifiers);
+ public void setIdentifiers(Collection<? extends Identifier> newValues) {
+ newValues = NonMarshalledAuthority.setMarshallables(identifiers, newValues);
identifiers = writeCollection(newValues, identifiers, Identifier.class);
- NonMarshalledAuthority.replace(identifiers, oldIds);
}
/**
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -63,7 +63,7 @@ import static org.apache.sis.internal.me
* @author Cédric Briançon (Geomatys)
* @author Rémi Maréchal (Geomatys)
* @since 0.3
- * @version 0.5
+ * @version 0.7
* @module
*/
@XmlType(name = "CI_Citation_Type", propOrder = {
@@ -364,29 +364,25 @@ public class DefaultCitation extends ISO
@Override
@XmlElement(name = "identifier")
public Collection<Identifier> getIdentifiers() {
- return NonMarshalledAuthority.excludeOnMarshalling(super.getIdentifiers());
+ return NonMarshalledAuthority.filterOnMarshalling(super.getIdentifiers());
}
/**
* Sets the unique identifier for the resource.
* Example: Universal Product Code (UPC), National Stock Number (NSN).
*
- * <p>This method overwrites all previous identifiers with the given new values,
- * <strong>except</strong> the XML identifiers ({@linkplain IdentifierSpace#ID ID},
- * {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>), ISBN and ISSN codes, if any.
- * We do not overwrite the XML identifiers because they are usually associated to object
- * identity, and we do not overwrite ISBN/ISSN codes because they have dedicated setters
- * for compliance with the ISO 19115 model.</p>
+ * <p>XML identifiers ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>),
+ * {@linkplain #getISBN() ISBN} and {@linkplain #getISSN() ISSN} codes are not affected by this method, unless
+ * they are explicitely provided in the given collection.</p>
*
* @param newValues The new identifiers, or {@code null} if none.
*
* @see #setISBN(String)
* @see #setISSN(String)
*/
- public void setIdentifiers(final Collection<? extends Identifier> newValues) {
- final Collection<Identifier> oldIds = NonMarshalledAuthority.filteredCopy(identifiers);
+ public void setIdentifiers(Collection<? extends Identifier> newValues) {
+ newValues = NonMarshalledAuthority.setMarshallables(identifiers, newValues);
identifiers = writeCollection(newValues, identifiers, Identifier.class);
- NonMarshalledAuthority.replace(identifiers, oldIds);
}
/**
Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFraction.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -26,7 +26,9 @@ import javax.xml.bind.annotation.adapter
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.identification.RepresentativeFraction;
-import org.apache.sis.internal.jaxb.IdentifierMapWithSpecialCases;
+import org.apache.sis.metadata.UnmodifiableMetadataException;
+import org.apache.sis.internal.jaxb.ModifiableIdentifierMap;
+import org.apache.sis.internal.jaxb.IdentifierMapAdapter;
import org.apache.sis.internal.jaxb.gco.GO_Integer64;
import org.apache.sis.internal.util.CheckedArrayList;
import org.apache.sis.measure.ValueRange;
@@ -63,14 +65,14 @@ import static org.apache.sis.internal.me
* @author Cédric Briançon (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.6
+ * @version 0.7
* @module
*
* @see DefaultResolution#getEquivalentScale()
*/
@XmlType(name = "MD_RepresentativeFraction_Type")
@XmlRootElement(name = "MD_RepresentativeFraction")
-public class DefaultRepresentativeFraction extends Number implements RepresentativeFraction, IdentifiedObject, Emptiable {
+public class DefaultRepresentativeFraction extends Number implements RepresentativeFraction, IdentifiedObject, Emptiable, Cloneable {
/**
* Serial number for compatibility with different versions.
*/
@@ -88,6 +90,11 @@ public class DefaultRepresentativeFracti
private Collection<Identifier> identifiers;
/**
+ * {@code true} if this representative fraction has been made unmodifiable.
+ */
+ private transient boolean isUnmodifiable;
+
+ /**
* Creates a uninitialized representative fraction.
* The {@linkplain #getDenominator() denominator} is initially zero
* and the {@linkplain #doubleValue() double value} is NaN.
@@ -161,6 +168,9 @@ public class DefaultRepresentativeFracti
* @throws IllegalArgumentException if the given value is negative.
*/
public void setDenominator(final long denominator) {
+ if (isUnmodifiable) {
+ throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ }
if (ensurePositive(DefaultRepresentativeFraction.class, "denominator", false, denominator)) {
this.denominator = denominator;
}
@@ -176,6 +186,9 @@ public class DefaultRepresentativeFracti
* @throws IllegalArgumentException if the given scale is our of range.
*/
public void setScale(final double scale) {
+ if (isUnmodifiable) {
+ throw new UnmodifiableMetadataException(Errors.format(Errors.Keys.UnmodifiableMetadata));
+ }
/*
* For the following argument check, we do not need to use a Metadatautility method because
* 'setScale' is never invoked at (un)marshalling time. Note also that we accept NaN values
@@ -259,6 +272,35 @@ public class DefaultRepresentativeFracti
}
/**
+ * Makes this representative fraction unmodifiable. After invocation to this method,
+ * any call to a setter method will throw an {@link UnmodifiableMetadataException}.
+ *
+ * @since 0.7
+ *
+ * @see org.apache.sis.metadata.ModifiableMetadata#freeze()
+ */
+ public void freeze() {
+ isUnmodifiable = true;
+ }
+
+ /**
+ * Returns a modifiable copy of this representative fraction.
+ *
+ * @return A modifiable copy of this representative fraction.
+ */
+ @Override
+ public DefaultRepresentativeFraction clone() {
+ final DefaultRepresentativeFraction c;
+ try {
+ c = (DefaultRepresentativeFraction) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new AssertionError(e); // Should never happen since we are cloneable.
+ }
+ c.isUnmodifiable = false;
+ return c;
+ }
+
+ /**
* Compares this object with the specified value for equality.
*
* @param object The object to compare with.
@@ -326,7 +368,9 @@ public class DefaultRepresentativeFracti
*/
@Override
public IdentifierMap getIdentifierMap() {
- return new IdentifierMapWithSpecialCases(getIdentifiers());
+ final Collection<Identifier> identifiers = getIdentifiers();
+ return isUnmodifiable ? new IdentifierMapAdapter(identifiers)
+ : new ModifiableIdentifierMap(identifiers);
}
Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/TreeTableFormatTest.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -20,14 +20,13 @@ import java.util.Arrays;
import javax.measure.unit.SI;
import org.opengis.metadata.citation.Role;
import org.opengis.metadata.citation.PresentationForm;
-import org.opengis.util.InternationalString;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTableFormat;
import org.apache.sis.util.iso.SimpleInternationalString;
import org.apache.sis.metadata.iso.content.DefaultBand;
import org.apache.sis.metadata.iso.content.DefaultImageDescription;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
-import org.apache.sis.metadata.iso.citation.DefaultIndividual;
+import org.apache.sis.metadata.iso.citation.DefaultCitationTest;
import org.apache.sis.metadata.iso.citation.DefaultResponsibility;
import org.apache.sis.metadata.iso.content.DefaultAttributeGroup;
import org.apache.sis.metadata.iso.identification.DefaultDataIdentification;
@@ -76,53 +75,41 @@ public final strictfp class TreeTableFor
}
/**
- * Creates the citation to use for testing purpose.
- */
- private static DefaultCitation createCitation() {
- final DefaultCitation citation = new DefaultCitation();
- final InternationalString title = new SimpleInternationalString("Undercurrent");
- citation.setTitle(title);
- citation.setISBN("9782505004509");
- citation.setPresentationForms(asList(
- PresentationForm.DOCUMENT_HARDCOPY,
- PresentationForm.IMAGE_HARDCOPY));
- citation.setAlternateTitles(asList(
- new SimpleInternationalString("Alt A"),
- new SimpleInternationalString("Alt B")));
- citation.setCitedResponsibleParties(asList(
- new DefaultResponsibility(Role.AUTHOR, null, new DefaultIndividual("Testsuya Toyoda", null, null)),
- new DefaultResponsibility(null, null, new DefaultIndividual("A japanese author", null, null))));
- return citation;
- }
-
- /**
* Tests the formatting of a {@link DefaultCitation} object.
*/
@Test
public void testCitation() {
- final DefaultCitation citation = createCitation();
+ final DefaultCitation citation = DefaultCitationTest.create();
final String text = format.format(citation.asTreeTable());
assertMultilinesEquals(
"Citation\n" +
- " ├─Title……………………………………………………………………… Undercurrent\n" +
- " ├─Alternate title (1 of 2)…………………… Alt A\n" +
- " ├─Alternate title (2 of 2)…………………… Alt B\n" +
+ " ├─Title…………………………………………………………………………… Undercurrent\n" +
+ " ├─Alternate title………………………………………………… Andākarento\n" +
" ├─Identifier\n" +
- " │ ├─Code……………………………………………………………… 9782505004509\n" +
+ " │ ├─Code…………………………………………………………………… 9782505004509\n" +
" │ ├─Authority\n" +
- " │ │ ├─Title………………………………………………… International Standard Book Number\n" +
- " │ │ └─Alternate title……………………… ISBN\n" +
- " │ └─Code space……………………………………………… ISBN\n"+
+ " │ │ ├─Title……………………………………………………… International Standard Book Number\n" +
+ " │ │ └─Alternate title…………………………… ISBN\n" +
+ " │ └─Code space…………………………………………………… ISBN\n"+
" ├─Cited responsible party (1 of 2)\n" +
" │ ├─Party\n" +
- " │ │ └─Name…………………………………………………… Testsuya Toyoda\n" +
- " │ └─Role……………………………………………………………… Author\n" +
+ " │ │ └─Name………………………………………………………… Testsuya Toyoda\n" +
+ " │ └─Role…………………………………………………………………… Author\n" +
" ├─Cited responsible party (2 of 2)\n" +
- " │ └─Party\n" +
- " │ └─Name…………………………………………………… A japanese author\n" +
- " ├─Presentation form (1 of 2)……………… Document hardcopy\n" +
- " ├─Presentation form (2 of 2)……………… Image hardcopy\n" +
- " └─ISBN………………………………………………………………………… 9782505004509\n", text);
+ " │ ├─Party\n" +
+ " │ │ └─Name………………………………………………………… Kōdansha\n" +
+ " │ ├─Role…………………………………………………………………… Editor\n" +
+ " │ └─Extent\n" +
+ " │ ├─Description……………………………………… World\n" +
+ " │ └─Geographic element\n" +
+ " │ ├─West bound longitude…… 180°W\n" +
+ " │ ├─East bound longitude…… 180°E\n" +
+ " │ ├─South bound latitude…… 90°S\n" +
+ " │ ├─North bound latitude…… 90°N\n" +
+ " │ └─Extent type code……………… true\n" +
+ " ├─Presentation form (1 of 2)…………………… Document digital\n" +
+ " ├─Presentation form (2 of 2)…………………… Document hardcopy\n" +
+ " └─ISBN……………………………………………………………………………… 9782505004509\n", text);
}
/**
Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/citation/DefaultCitationTest.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -18,12 +18,22 @@ package org.apache.sis.metadata.iso.cita
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
import org.opengis.metadata.Identifier;
+import org.opengis.metadata.citation.Role;
+import org.opengis.metadata.citation.Responsibility;
+import org.opengis.metadata.citation.PresentationForm;
+import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.xml.IdentifierMap;
+import org.apache.sis.metadata.iso.extent.Extents;
import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.util.iso.DefaultInternationalString;
import org.apache.sis.test.TestCase;
import org.junit.Test;
+import static org.apache.sis.test.TestUtilities.getSingleton;
import static org.junit.Assert.*;
@@ -32,11 +42,36 @@ import static org.junit.Assert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.7
* @module
*/
public final strictfp class DefaultCitationTest extends TestCase {
/**
+ * Creates a citation with an arbitrary title, presentation form and other properties.
+ *
+ * @return An arbitrary citation.
+ *
+ * @since 0.7
+ */
+ public static DefaultCitation create() {
+ final DefaultCitation citation = new DefaultCitation();
+ final DefaultInternationalString title = new DefaultInternationalString();
+ title.add(Locale.JAPANESE, "アンダーカレント");
+ title.add(Locale.ENGLISH, "Undercurrent");
+ citation.setTitle(title);
+ citation.setISBN("9782505004509");
+ citation.setPresentationForms(Arrays.asList(
+ PresentationForm.DOCUMENT_HARDCOPY,
+ PresentationForm.DOCUMENT_DIGITAL));
+ citation.setAlternateTitles(Collections.singleton(
+ new SimpleInternationalString("Andākarento"))); // Actually a different script of the Japanese title.
+ citation.setCitedResponsibleParties(Arrays.asList(
+ new DefaultResponsibility(Role.AUTHOR, null, new DefaultIndividual("Testsuya Toyoda", null, null)),
+ new DefaultResponsibility(Role.EDITOR, Extents.WORLD, new DefaultOrganisation("Kōdansha", null, null, null))));
+ return citation;
+ }
+
+ /**
* Tests the identifier map, which handles ISBN and ISSN codes in a special way.
*/
@Test
@@ -61,11 +96,67 @@ public final strictfp class DefaultCitat
citation.setIdentifiers(Arrays.asList(
new DefaultIdentifier(Citations.NETCDF, "MyNetCDF"),
new DefaultIdentifier(Citations.EPSG, "MyEPSG"),
- new DefaultIdentifier(Citations.ISBN, "MyIgnored"),
+ new DefaultIdentifier(Citations.ISBN, "NewISBN"),
new DefaultIdentifier(Citations.ISSN, "MyISSN")));
- assertEquals("The ISBN value shall not have been overwritten.", "MyISBN", citation.getISBN());
+ assertEquals("The ISBN value shall have been overwritten.", "NewISBN", citation.getISBN());
assertEquals("The ISSN value shall have been added, because new.", "MyISSN", citation.getISSN());
- assertEquals("{NetCDF=“MyNetCDF”, EPSG=“MyEPSG”, ISSN=“MyISSN”, ISBN=“MyISBN”}", identifierMap.toString());
+ assertEquals("{NetCDF=“MyNetCDF”, EPSG=“MyEPSG”, ISBN=“NewISBN”, ISSN=“MyISSN”}", identifierMap.toString());
+ }
+
+ /**
+ * Tests {@link DefaultCitation#freeze()}, which is needed for the constants defined in {@link Citations}.
+ */
+ @Test
+ public void testFreeze() {
+ final DefaultCitation original = create();
+ final DefaultCitation clone = (DefaultCitation) original.unmodifiable(); // This will invoke 'freeze()'.
+ assertNotSame(original, clone);
+ assertTrue ("original.isModifiable", original.isModifiable());
+ assertFalse( "clone.isModifiable", clone.isModifiable());
+ assertSame ("original.unmodifiable", clone, original.unmodifiable());
+ assertSame ( "clone.unmodifiable", clone, clone.unmodifiable());
+
+ assertSame ("ISBN", original.getISBN(), clone.getISBN());
+ assertSame ("title", original.getTitle(), clone.getTitle());
+ assertSame ("alternateTitle", getSingleton(original.getAlternateTitles()),
+ getSingleton(clone.getAlternateTitles()));
+
+ assertCopy(original.getIdentifiers(), clone.getIdentifiers());
+ assertCopy(original.getCitedResponsibleParties(), clone.getCitedResponsibleParties());
+ assertCopy(original.getPresentationForms(), clone.getPresentationForms());
+ /*
+ * Verify the unique identifier, which is the ISBN code. ISBN and ISSN codes are handled
+ * in a special way by DefaultCitation (they are instances of SpecializedIdentifier), but
+ * the should nevertheless be cloned.
+ */
+ final Identifier ide = getSingleton(original.getIdentifiers());
+ final Identifier ida = getSingleton( clone.getIdentifiers());
+ assertNotSame("identifier", ide, ida);
+ assertSame("code", ide.getCode(), ida.getCode());
+ assertSame("authority", ide.getAuthority(), ida.getAuthority());
+ /*
+ * Verify the author metadata.
+ */
+ final Responsibility re = CollectionsExt.first(original.getCitedResponsibleParties());
+ final Responsibility ra = CollectionsExt.first(clone .getCitedResponsibleParties());
+ assertNotSame("citedResponsibleParty", re, ra);
+ assertSame("role", re.getRole(), ra.getRole());
+ assertSame("name", getSingleton(re.getParties()).getName(),
+ getSingleton(ra.getParties()).getName());
+ }
+
+ /**
+ * Verifies that {@code actual} is an unmodifiable copy of {@code expected}.
+ */
+ private static <T> void assertCopy(final Collection<T> expected, final Collection<T> actual) {
+ assertNotSame("ModifiableMetadata.freeze() shall have copied the collection.", expected, actual);
+ assertEquals("The copied collection shall have the same content than the original.", expected, actual);
+ try {
+ actual.add(null);
+ fail("The copied collection shall be unmodifiable.");
+ } catch (UnsupportedOperationException e) {
+ // This is the expected exception.
+ }
}
}
Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFractionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFractionTest.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFractionTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/identification/DefaultRepresentativeFractionTest.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -31,7 +31,7 @@ import static org.apache.sis.test.Assert
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.4
- * @version 0.4
+ * @version 0.7
* @module
*/
public final strictfp class DefaultRepresentativeFractionTest extends TestCase {
@@ -72,4 +72,27 @@ public final strictfp class DefaultRepre
*/
assertEquals(fraction, XML.unmarshal(xml));
}
+
+ /**
+ * Tests indirectly {@link DefaultRepresentativeFraction#freeze()}.
+ * This method verifies that a call to {@link DefaultResolution#freeze()}
+ * implies a call to {@link DefaultRepresentativeFraction#freeze()}.
+ *
+ * @since 0.7
+ */
+ @Test
+ public void testFreeze() {
+ final DefaultRepresentativeFraction fraction = new DefaultRepresentativeFraction(1000);
+ final DefaultResolution resolution = new DefaultResolution(fraction);
+ resolution.freeze();
+ final DefaultRepresentativeFraction clone = (DefaultRepresentativeFraction) resolution.getEquivalentScale();
+ assertEquals ("Fraction should have the same value.", fraction, clone);
+ assertNotSame("Should have copied the fraction instance.", fraction, clone);
+ try {
+ clone.setDenominator(10);
+ fail("Shall not be allowed to modify an unmodifiable fraction.");
+ } catch (UnsupportedOperationException e) {
+ // This is the expected exception.
+ }
+ }
}
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -36,11 +36,16 @@ import static java.lang.Math.*;
* (Source: §1.3.3 in IOGP Publication 373-7-2 – Geomatics Guidance Note number 7, part 2 – April 2015).
* Indeed, those two projections have some equation in commons which are provided in this base class.</p>
*
- * <p>The polar stereographic projection is not documented as a special case of Lambert Conic Conformal,
+ * <p>The Polar Stereographic projection is not documented as a special case of Lambert Conic Conformal,
* but the equations in the {@code PolarStereographic.transform(…)} and {@code inverseTransform(…)} methods
* appear to be the same with the <var>n</var> factor fixed to 1 or -1, so we leverage the code provided by
* this base class. This class hierarchy is only an implementation convenience and not part of public API.</p>
*
+ * <p>The Transverse Mercator projection is also conformal, but does not use the formulas provided in this class.
+ * It will instead compute the coefficients itself and use its own, more complex, formulas with those coefficients.
+ * However the formulas provided in this {@code ConformalProjection} class can be seen as a special case of
+ * Transverse Mercator formulas for <var>x</var> = 0.</p>
+ *
* <div class="note"><b>Reference:</b>
* “Lambert developed the regular Conformal Conic as the oblique aspect of a family containing the previously
* known polar Stereographic and regular Mercator projections. (…) If the standard parallels are symmetrical
@@ -49,7 +54,7 @@ import static java.lang.Math.*;
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*/
abstract class ConformalProjection extends NormalizedProjection {
@@ -59,63 +64,98 @@ abstract class ConformalProjection exten
private static final long serialVersionUID = 458860570536642265L;
/**
- * The threshold value of {@link #excentricity} at which we consider the accuracy of the
- * series expansion insufficient. This threshold is determined empirically with the help
- * of the {@code MercatorMethodComparison} class in the test directory.
- * We choose the value where:
+ * {@code false} for using the original formulas as published by EPSG, or {@code true} for using formulas
+ * modified using trigonometric identities. The use of trigonometric identities is for reducing the amount
+ * of calls to the {@link Math#sin(double)} and similar methods. Some identities used are:
*
* <ul>
- * <li>the average error of series expansion become greater than {@link NormalizedProjection#ITERATION_TOLERANCE},</li>
- * <li>the maximal error of series expansion become greater than {@link NormalizedProjection#ANGULAR_TOLERANCE}.</li>
+ * <li>sin(2θ) = 2⋅sinθ⋅cosθ</li>
+ * <li>cos(2θ) = cos²θ - sin²θ</li>
+ * <li>sin(3θ) = (3 - 4⋅sin²θ)⋅sinθ</li>
+ * <li>cos(3θ) = (4⋅cos³θ) - 3⋅cosθ</li>
+ * <li>sin(4θ) = (4 - 8⋅sin²θ)⋅sinθ⋅cosθ</li>
+ * <li>cos(4θ) = (8⋅cos⁴θ) - (8⋅cos²θ) + 1</li>
* </ul>
- */
- static final double EXCENTRICITY_THRESHOLD = 0.16;
-
- /**
- * Whether to use the original formulas a published by EPSG, or their form modified using trigonometric identities.
- * The modified form uses trigonometric identifies for reducing the amount of calls to the {@link Math#sin(double)}
- * method. The identities used are:
+ *
+ * Hyperbolic formulas (used in Transverse Mercator projection):
*
* <ul>
- * <li>sin(2⋅x) = 2⋅sin(x)⋅cos(x)</li>
- * <li>sin(3⋅x) = (3 - 4⋅sin²(x))⋅sin(x)</li>
- * <li>sin(4⋅x) = (4 - 8⋅sin²(x))⋅sin(x)⋅cos(x)</li>
+ * <li>sinh(2θ) = 2⋅sinhθ⋅coshθ</li>
+ * <li>cosh(2θ) = cosh²θ + sinh²θ = 2⋅cosh²θ - 1 = 1 + 2⋅sinh²θ</li>
+ * <li>sinh(3θ) = (3 + 4⋅sinh²θ)⋅sinhθ</li>
+ * <li>cosh(3θ) = ((4⋅cosh²θ) - 3)⋅coshθ</li>
+ * <li>sinh(4θ) = (1 + 2⋅sinh²θ)⋅4.sinhθ⋅coshθ
+ * = 4.cosh(2θ).sinhθ⋅coshθ</li>
+ * <li>cosh(4θ) = (8⋅cosh⁴θ) - (8⋅cosh²θ) + 1
+ * = 8⋅cosh²(θ) ⋅ (cosh²θ - 1) + 1
+ * = 8⋅cosh²(θ) ⋅ sinh²(θ) + 1
+ * = 2⋅sinh²(2θ) + 1</li>
* </ul>
*
* Note that since this boolean is static final, the compiler should exclude the code in the branch that is never
* executed (no need to comment-out that code).
+ *
+ * @see #identityEquals(double, double)
*/
- private static final boolean ORIGINAL_FORMULA = false;
+ static final boolean ALLOW_TRIGONOMETRIC_IDENTITIES = true;
/**
- * Coefficients in the series expansion used by {@link #φ(double)}.
+ * The threshold value of {@link #excentricity} at which we consider the accuracy of the
+ * series expansion insufficient. This threshold is determined empirically with the help
+ * of the {@code MercatorMethodComparison} class in the test directory.
+ * We choose the value where:
*
- * <p>Consider those fields as final. They are not only of the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ * <ul>
+ * <li>the average error of series expansion become greater than {@link NormalizedProjection#ITERATION_TOLERANCE},</li>
+ * <li>the maximal error of series expansion become greater than {@link NormalizedProjection#ANGULAR_TOLERANCE}.</li>
+ * </ul>
*/
- private transient double c2χ, c4χ, c6χ, c8χ;
+ static final double EXCENTRICITY_THRESHOLD = 0.16;
/**
* {@code true} if the {@link #excentricity} value is greater than or equals to {@link #EXCENTRICITY_THRESHOLD},
* in which case the {@link #φ(double)} method will need to use an iterative method.
*
- * <p>Consider this field as final. It is not only of the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ * <p><strong>Consider this field as final!</strong>
+ * It is not final only for the purpose of {@link #readObject(ObjectInputStream)}.</p>
*/
private transient boolean useIterations;
/**
+ * Coefficients in the series expansion of the inverse projection,
+ * depending only on {@linkplain #excentricity excentricity} value.
+ * The series expansion is of the following form, where f(θ) is typically sin(θ):
+ *
+ * <blockquote>ci₂⋅f(2θ) + ci₄⋅f(4θ) + ci₆⋅f(6θ) + ci₈⋅f(8θ)</blockquote>
+ *
+ * This {@code ConformalProjection} class uses those coefficients in {@link #φ(double)}.
+ * However some subclasses may compute those coefficients differently and use them in a
+ * different series expansion (but for the same purpose).
+ *
+ * <p><strong>Consider those fields as final!</strong> They are not final only for sub-class
+ * constructors convenience and for the purpose of {@link #readObject(ObjectInputStream)}.</p>
+ *
+ * @see #computeCoefficients()
+ */
+ transient double ci2, ci4, ci6, ci8;
+
+ /**
* Creates a new normalized projection from the parameters computed by the given initializer.
*
+ * <p>It is sub-classes responsibility to invoke {@code super.computeCoefficients()} in their
+ * constructor when ready, or to compute the coefficients themselves.</p>
+ *
* @param initializer The initializer for computing map projection internal parameters.
*/
ConformalProjection(final Initializer initializer) {
super(initializer);
- initialize();
}
/**
- * Computes the transient fields after construction or deserialization.
+ * Computes the coefficients in the series expansions from the {@link #excentricitySquared} value.
+ * This method shall be invoked after {@code ConformalProjection} construction or deserialization.
*/
- private void initialize() {
+ void computeCoefficients() {
useIterations = (excentricity >= EXCENTRICITY_THRESHOLD);
final double e2 = excentricitySquared;
final double e4 = e2 * e2;
@@ -125,14 +165,20 @@ abstract class ConformalProjection exten
* For each line below, add the smallest values first in order to reduce rounding errors.
* The smallest values are the one using the excentricity raised to the highest power.
*/
- c2χ = 13/ 360.* e8 + 1/ 12.* e6 + 5/24.* e4 + e2/2;
- c4χ = 811/ 11520.* e8 + 29/240.* e6 + 7/48.* e4;
- c6χ = 81/ 1120.* e8 + 7/120.* e6;
- c8χ = 4279/161280.* e8;
- if (!ORIGINAL_FORMULA) {
- c4χ *= 2;
- c6χ *= 4;
- c8χ *= 8;
+ ci2 = 13/ 360.* e8 + 1/ 12.* e6 + 5/24.* e4 + e2/2;
+ ci4 = 811/ 11520.* e8 + 29/240.* e6 + 7/48.* e4;
+ ci6 = 81/ 1120.* e8 + 7/120.* e6;
+ ci8 = 4279/161280.* e8;
+ /*
+ * When rewriting equations using trigonometric identities, some constants appear.
+ * For example sin(2θ) = 2⋅sinθ⋅cosθ, so we can factor out the 2 constant into the
+ * corresponding 'c' field.
+ */
+ if (ALLOW_TRIGONOMETRIC_IDENTITIES) {
+ // Multiplication by powers of 2 does not bring any additional rounding error.
+ ci4 *= 2;
+ ci6 *= 4;
+ ci8 *= 8;
}
}
@@ -145,10 +191,10 @@ abstract class ConformalProjection exten
ConformalProjection(final ConformalProjection other) {
super(other);
useIterations = other.useIterations;
- c2χ = other.c2χ;
- c4χ = other.c4χ;
- c6χ = other.c6χ;
- c8χ = other.c8χ;
+ ci2 = other.ci2;
+ ci4 = other.ci4;
+ ci6 = other.ci6;
+ ci8 = other.ci8;
}
/**
@@ -156,6 +202,12 @@ abstract class ConformalProjection exten
* This formula is also part of other projections, since Mercator can be considered as a special case of
* Lambert Conic Conformal for instance.
*
+ * <div class="note"><b>Warning:</b>
+ * this method is valid only if the series expansion coefficients where computed by the
+ * {@link #computeCoefficients()} method defined in this class. This is not the case of
+ * {@link TransverseMercator} for instance. Note however that even in the later case,
+ * this method can still be seen as a special case of {@code TransverseMercator} formulas.</div>
+ *
* <p>This function is <em>almost</em> the converse of the {@link #expOfNorthing(double, double)} function.
* In a Mercator inverse projection, the value of the {@code expOfSouthing} argument is {@code exp(-y)}.</p>
*
@@ -196,23 +248,20 @@ abstract class ConformalProjection exten
* EPSG guidance note. Note that we add those terms in reverse order, beginning with the smallest
* values, for reducing rounding errors due to IEEE 754 arithmetic.
*/
- if (ORIGINAL_FORMULA) {
- φ += c8χ * sin(8*φ)
- + c6χ * sin(6*φ)
- + c4χ * sin(4*φ)
- + c2χ * sin(2*φ);
+ if (!ALLOW_TRIGONOMETRIC_IDENTITIES) {
+ φ += ci8 * sin(8*φ)
+ + ci6 * sin(6*φ)
+ + ci4 * sin(4*φ)
+ + ci2 * sin(2*φ);
} else {
/*
* Same formula than above, be rewriten using trigonometric identities in order to have only two
- * calls to Math.sin/cos instead than 5. The performance gain is twice faster on some machines.
+ * calls to Math.sin/cos instead than 5. The performance gain is twice faster on tested machine.
*/
- final double sin2χ = sin(2*φ);
- final double sin_cos2χ = cos(2*φ) * sin2χ;
- final double sin_sin2χ = sin2χ * sin2χ;
- φ += c8χ * (0.50 - sin_sin2χ)*sin_cos2χ // ÷8 compared to original formula
- + c6χ * (0.75 - sin_sin2χ)*sin2χ // ÷4 compared to original formula
- + c4χ * ( sin_cos2χ) // ÷2 compared to original formula
- + c2χ * sin2χ;
+ final double sin_2φ = sin(2*φ);
+ final double sin2 = sin_2φ * sin_2φ;
+ φ += ((ci4 + ci8 * (0.50 - sin2)) * cos(2*φ)
+ + (ci2 + ci6 * (0.75 - sin2))) * sin_2φ;
}
/*
* Note: a previous version checked if the value of the smallest term c8χ⋅sin(8φ) was smaller than
@@ -336,6 +385,18 @@ abstract class ConformalProjection exten
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
- initialize();
+ computeCoefficients();
+ }
+
+ /**
+ * Verifies if a trigonometric identity produced the expected value. This method is used in assertions only,
+ * for values close to the [-1 … +1] range. The tolerance threshold is approximatively 1.5E-12 (note that it
+ * still about 7000 time greater than {@code Math.ulp(1.0)}).
+ *
+ * @see #ALLOW_TRIGONOMETRIC_IDENTITIES
+ */
+ static boolean identityEquals(final double actual, final double expected) {
+ return !(abs(actual - expected) > // Use !(a > b) instead of (a <= b) in order to tolerate NaN.
+ (ANGULAR_TOLERANCE / 1000) * max(1, abs(expected))); // Increase tolerance for values outside the [-1 … +1] range.
}
}
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -211,6 +211,7 @@ public class LambertConicConformal exten
@Workaround(library="JDK", version="1.7")
private LambertConicConformal(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
double φ0 = initializer.getAndStore(((initializer.variant & 1) != 0) ? // Odd 'type' are SP1, even 'type' are SP2.
LambertConformal1SP.LATITUDE_OF_ORIGIN : LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN);
/*
@@ -368,7 +369,7 @@ public class LambertConicConformal exten
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -202,6 +202,7 @@ public class Mercator extends ConformalP
@Workaround(library="JDK", version="1.7")
private Mercator(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
this.variant = initializer.variant;
/*
* The "Longitude of natural origin" parameter is found in all Mercator projections and is mandatory.
@@ -303,7 +304,7 @@ public class Mercator extends ConformalP
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -116,7 +116,7 @@ import org.apache.sis.internal.jdk7.Obje
* @author Rueben Schulz (UBC)
* @author Rémi Maréchal (Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*
* @see ContextualParameters
@@ -456,7 +456,7 @@ public abstract class NormalizedProjecti
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
* Conversion to other units and {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes
* changes in axis order} are <strong>not</strong> managed by the returned transform.
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java?rev=1707299&r1=1707298&r2=1707299&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java [UTF-8] Wed Oct 7 13:30:49 2015
@@ -134,6 +134,7 @@ public class PolarStereographic extends
@Workaround(library="JDK", version="1.7")
private PolarStereographic(final Initializer initializer) {
super(initializer);
+ super.computeCoefficients();
final byte variant = initializer.variant;
/*
* "Standard parallel" and "Latitude of origin" should be mutually exclusive,
@@ -249,7 +250,7 @@ public class PolarStereographic extends
/**
* Returns the sequence of <cite>normalization</cite> → {@code this} → <cite>denormalization</cite> transforms
- * as a whole. The transform returned by this method except (<var>longitude</var>, <var>latitude</var>)
+ * as a whole. The transform returned by this method expects (<var>longitude</var>, <var>latitude</var>)
* coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) coordinates in <em>metres</em>.
*
* <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
|