Author: desruisseaux
Date: Thu Feb 28 23:09:22 2013
New Revision: 1451422
URL: http://svn.apache.org/r1451422
Log:
Ported a little bit more of metadata internal mechanics.
Added:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
(with props)
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
(with props)
Modified:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
Added:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1451422&view=auto
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
(added)
+++
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata;
+
+import java.util.logging.Logger;
+import java.lang.reflect.Modifier;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.LenientComparable;
+import org.apache.sis.util.logging.Logging;
+
+
+/**
+ * Base class for metadata implementations, providing basic operations using
Java reflection.
+ * Available operations include the {@linkplain #AbstractMetadata(Object) copy
constructor},
+ * together with {@link #equals(Object)} and {@link #hashCode()}
implementations.
+ *
+ * {@section Requirements for subclasses}
+ * Subclasses need to implement the interfaces of some {@linkplain
MetadataStandard metadata standard}
+ * and return that standard in the {@link #getStandard()} method.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+@ThreadSafe
+public abstract class AbstractMetadata implements LenientComparable {
+ /**
+ * The logger for messages related to metadata implementations.
+ */
+ protected static final Logger LOGGER =
Logging.getLogger(AbstractMetadata.class);
+
+ /**
+ * Creates an initially empty metadata.
+ */
+ protected AbstractMetadata() {
+ }
+
+ /**
+ * Returns the class of the given metadata, ignoring SIS private classes
+ * like {@link org.apache.sis.metadata.iso.citation.CitationConstant}.
+ *
+ * @see <a href="http://jira.geotoolkit.org/browse/GEOTK-48">GEOTK-48</a>
+ */
+ private static Class<?> getClass(final Object metadata) {
+ Class<?> type = metadata.getClass();
+ while (!Modifier.isPublic(type.getModifiers()) &&
type.getName().startsWith("org.geotoolkit.metadata.iso.")) { // TODO
+ type = type.getSuperclass();
+ }
+ return type;
+ }
+
+ /**
+ * Compares this metadata with the specified object for equality. The
default
+ * implementation uses Java reflection. Subclasses may override this method
+ * for better performances, or for comparing "hidden" attributes not
specified
+ * by the GeoAPI (or other standard) interface.
+ *
+ * <p>This method performs a <cite>deep</cite> comparison: if this
metadata contains
+ * other metadata, then the comparison will invoke the {@link
Object#equals(Object)}
+ * method on those children as well.</p>
+ *
+ * @param object The object to compare with this metadata.
+ * @param mode The strictness level of the comparison.
+ * @return {@code true} if the given object is equal to this metadata.
+ */
+ @Override
+ public boolean equals(final Object object, final ComparisonMode mode) {
+ if (object == this) {
+ return true;
+ }
+ if (mode == ComparisonMode.STRICT) {
+ if (object == null || getClass(object) != getClass(this)) {
+ return false;
+ }
+ }
+ // TODO: There is some code to port here.
+ /*
+ * DEADLOCK WARNING: A deadlock may occur if the same pair of objects
is being compared
+ * in an other thread (see http://jira.codehaus.org/browse/GEOT-1777).
Ideally we would
+ * synchronize on 'this' and 'object' atomically (RFE #4210659). Since
we can't in Java
+ * a workaround is to always get the locks in the same order.
Unfortunately we have no
+ * guarantee that the caller didn't looked the object himself. For now
the safest approach
+ * is to not synchronize at all.
+ */
+ // TODO: There is some code to port here.
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+
+ /**
+ * Performs a {@linkplain ComparisonMode#STRICT strict} comparison of this
metadata with
+ * the given object.
+ *
+ * @param object The object to compare with this metadata for equality.
+ */
+ @Override
+ public final boolean equals(final Object object) {
+ return equals(object, ComparisonMode.STRICT);
+ }
+
+ /**
+ * Computes a hash code value for this metadata using Java reflection. The
hash code
+ * is defined as the sum of hash code values of all non-null properties.
This is the
+ * same contract than {@link java.util.Set#hashCode()} and ensure that the
hash code
+ * value is insensitive to the ordering of properties.
+ */
+ @Override
+ public synchronized int hashCode() {
+ // TODO: There is some code to port here.
+ throw new UnsupportedOperationException("Not yet implemented.");
+ }
+}
Propchange:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Added:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java?rev=1451422&view=auto
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
(added)
+++
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.metadata;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import org.apache.sis.util.collection.CollectionsExt;
+import org.apache.sis.util.collection.UnmodifiableArrayList;
+
+
+/**
+ * Returns unmodifiable view of metadata elements of arbitrary type.
+ * Despite the {@code Cloner} class name, this class actually tries
+ * to avoid creating new clones as much as possible.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.3 (derived from geotk-2.1)
+ * @version 0.3
+ * @module
+ */
+final class Cloner extends org.apache.sis.internal.util.Cloner {
+ /**
+ * Creates a new {@code Cloner} instance.
+ */
+ Cloner() {
+ }
+
+ /**
+ * Tells {@link #clone(Object)} to return the original object
+ * if no public {@code clone()} method is found.
+ */
+ @Override
+ protected boolean isCloneRequired(final Object object) {
+ return false;
+ }
+
+ /**
+ * Returns an unmodifiable copy of the specified object.
+ * This method performs the following heuristic tests:
+ *
+ * <ul>
+ * <li>If the specified object is an instance of {@code
ModifiableMetadata},
+ * then {@link ModifiableMetadata#unmodifiable()} is invoked on that
object.</li>
+ * <li>Otherwise, if the object is a {@linkplain Collection collection},
then the
+ * content is copied into a new collection of similar type, with
values replaced
+ * by their unmodifiable variant.</li>
+ * <li>Otherwise, if the object implements the {@link Cloneable}
interface,
+ * then a clone is returned.</li>
+ * <li>Otherwise, the object is assumed immutable and returned
unchanged.</li>
+ * </ul>
+ *
+ * @param object The object to convert in an immutable one.
+ * @return A presumed immutable view of the specified object.
+ */
+ @Override
+ public Object clone(final Object object) {
+ /*
+ * CASE 1 - The object is an implementation of ModifiableMetadata. It
may have
+ * its own algorithm for creating an unmodifiable view of
metadata.
+ */
+ if (object instanceof ModifiableMetadata) {
+ return ((ModifiableMetadata) object).unmodifiable();
+ }
+ /*
+ * CASE 2 - The object is a collection. All elements are replaced by
their
+ * unmodifiable variant and stored in a new collection of
similar
+ * type.
+ */
+ 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;
+ }
+ } else {
+ final Object[] array = collection.toArray();
+ for (int i=0; i<array.length; i++) {
+ array[i] = clone(array[i]);
+ }
+ // Do not use the SIS Checked* classes since we don't
+ // need synchronization or type checking anymore.
+ if (isSet) {
+ collection = CollectionsExt.immutableSet(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).
+ collection = UnmodifiableArrayList.wrap(array);
+ }
+ }
+ return collection;
+ }
+ /*
+ * CASE 3 - The object is a map. Copies all entries in a new map and
replaces all values
+ * by their unmodifiable variant. The keys are assumed
already immutable.
+ */
+ if (object instanceof Map<?,?>) {
+ final Map<Object,Object> map = new LinkedHashMap<>((Map<?,?>)
object);
+ for (final Iterator<Map.Entry<Object,Object>>
it=map.entrySet().iterator(); it.hasNext();) {
+ final Map.Entry<Object,Object> entry = it.next();
+ entry.setValue(clone(entry.getValue()));
+ }
+ return CollectionsExt.unmodifiableOrCopy(map);
+ }
+ /*
+ * CASE 4 - The object is presumed cloneable.
+ */
+ if (object instanceof Cloneable) try {
+ return super.clone(object);
+ } catch (CloneNotSupportedException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ /*
+ * CASE 5 - Any other case. The object is assumed immutable and
returned unchanged.
+ */
+ return object;
+ }
+}
Propchange:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Cloner.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.NoSuchElementException;
import net.jcip.annotations.ThreadSafe;
import org.opengis.util.CodeList;
+import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.collection.CheckedHashSet;
import org.apache.sis.util.collection.CheckedArrayList;
@@ -44,29 +45,33 @@ import static org.apache.sis.util.collec
* <p>For singleton value:</p>
*
* {@preformat java
- * private Foo property;
+ * public class MyMetadata {
+ * private Foo property;
*
- * public synchronized Foo getProperty() {
- * return property;
- * }
+ * public synchronized Foo getProperty() {
+ * return property;
+ * }
*
- * public synchronized void setProperty(Foo newValue) {
- * checkWritePermission();
- * property = newValue;
+ * public synchronized void setProperty(Foo newValue) {
+ * checkWritePermission();
+ * property = newValue;
+ * }
* }
* }
*
* For collections (note that the call to {@link #checkWritePermission()} is
implicit):
*
* {@preformat java
- * private Collection<Foo> properties;
+ * public class MyMetadata {
+ * private Collection<Foo> properties;
*
- * public synchronized Collection<Foo> getProperties() {
- * return properties = nonNullCollection(properties, Foo.class);
- * }
+ * public synchronized Collection<Foo> getProperties() {
+ * return properties = nonNullCollection(properties, Foo.class);
+ * }
*
- * public synchronized void setProperties(Collection<Foo> newValues) {
- * properties = copyCollection(newValues, properties, Foo.class);
+ * public synchronized void setProperties(Collection<Foo> newValues) {
+ * properties = copyCollection(newValues, properties, Foo.class);
+ * }
* }
* }
*
@@ -76,7 +81,16 @@ import static org.apache.sis.util.collec
* @module
*/
@ThreadSafe
-public abstract class ModifiableMetadata {
+public abstract class ModifiableMetadata extends AbstractMetadata implements
Cloneable {
+ /**
+ * An unmodifiable copy of this metadata, created only when first needed.
+ * If {@code null}, then no unmodifiable entity is available.
+ * If {@code this}, then this entity is itself unmodifiable.
+ *
+ * @see #unmodifiable()
+ */
+ private transient ModifiableMetadata unmodifiable;
+
/**
* Constructs an initially empty metadata.
*/
@@ -93,8 +107,83 @@ public abstract class ModifiableMetadata
* @see #freeze()
* @see #checkWritePermission()
*/
- public final boolean isModifiable() {
- return true; // To be implemented later.
+ public final synchronized boolean isModifiable() {
+ return unmodifiable != this;
+ }
+
+ /**
+ * Returns an unmodifiable copy of this metadata. Any attempt to modify a
property of the
+ * returned object will throw an {@link UnmodifiableMetadataException}.
The state of this
+ * object is not modified.
+ *
+ * <p>This method is useful for reusing the same metadata object as a
template.
+ * For example:</p>
+ *
+ * {@preformat java
+ * DefaultCitation myCitation = new DefaultCitation();
+ * myCitation.setTitle(new SimpleInternationalString("The title of my
book"));
+ * myCitation.setEdition(new SimpleInternationalString("First
edition"));
+ * final Citation firstEdition = (Citation) myCitation.unmodifiable();
+ *
+ * myCitation.setEdition(new SimpleInternationalString("Second
edition"));
+ * final Citation secondEdition = (Citation) myCitation.unmodifiable();
+ * // The title of the second edition is unchanged compared to the
first edition.
+ * }
+ *
+ * The default implementation makes the following choice:
+ *
+ * <ul>
+ * <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>
+ *
+ * @return An unmodifiable copy of this metadata.
+ */
+ public synchronized AbstractMetadata unmodifiable() {
+ // Reminder: 'unmodifiable' is reset to null by checkWritePermission().
+ if (unmodifiable == null) {
+ final ModifiableMetadata candidate;
+ try {
+ /*
+ * Need a SHALLOW copy of this metadata, because some
attributes
+ * may already be unmodifiable and we don't want to clone them.
+ */
+ candidate = clone();
+ } catch (CloneNotSupportedException exception) {
+ /*
+ * The metadata is not cloneable for some reason left to the
user
+ * (for example it may be backed by some external database).
+ * Assumes that the metadata is unmodifiable.
+ */
+ Logging.unexpectedException(LOGGER, getClass(),
"unmodifiable", exception);
+ return this;
+ }
+ candidate.freeze();
+ // Set the field only after success. The 'unmodifiable' field must
+ // stay null if an exception occurred during clone() or freeze().
+ unmodifiable = candidate;
+ }
+ assert !unmodifiable.isModifiable();
+ return unmodifiable;
+ }
+
+ /**
+ * Declares this metadata and all its attributes as unmodifiable. Any
attempt to modify a
+ * 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
+ * performs its work using Java reflection.</p>
+ *
+ * @see #isModifiable()
+ * @see #checkWritePermission()
+ */
+ public synchronized void freeze() {
+ if (isModifiable()) {
+ throw new UnsupportedOperationException("Not yet implemented.");
// TODO
+ }
}
/**
@@ -112,6 +201,7 @@ public abstract class ModifiableMetadata
if (!isModifiable()) {
throw new UnmodifiableMetadataException("Unmodifiable metadata");
// TODO: localize
}
+ unmodifiable = null;
}
/**
@@ -137,7 +227,6 @@ public abstract class ModifiableMetadata
*
* @see #nonNullList(List, Class)
*/
- @SuppressWarnings("unchecked")
protected final <E> List<E> copyList(final Collection<? extends E> source,
List<E> target, final Class<E> elementType)
throws UnmodifiableMetadataException
@@ -182,7 +271,6 @@ public abstract class ModifiableMetadata
*
* @see #nonNullSet(Set, Class)
*/
- @SuppressWarnings("unchecked")
protected final <E> Set<E> copySet(final Collection<? extends E> source,
Set<E> target, final Class<E> elementType)
throws UnmodifiableMetadataException
@@ -233,7 +321,6 @@ public abstract class ModifiableMetadata
* elements, or {@code null} if the source was null.
* @throws UnmodifiableMetadataException if this metadata is unmodifiable.
*/
- @SuppressWarnings("unchecked")
protected final <E> Collection<E> copyCollection(final Collection<?
extends E> source,
Collection<E> target, final Class<E> elementType)
throws UnmodifiableMetadataException
@@ -451,4 +538,22 @@ public abstract class ModifiableMetadata
return (Class) (CodeList.class.isAssignableFrom(elementType) ||
Enum.class.isAssignableFrom(elementType) ?
Set.class : List.class);
}
+
+ /**
+ * Returns a shallow copy of this metadata.
+ *
+ * {@section Usage}
+ * While {@linkplain Cloneable cloneable}, this class do not provides the
{@code clone()}
+ * operation as part of the public API. The clone operation is required
for the internal
+ * working of the {@link #unmodifiable()} method, which needs
<strong>shallow</strong>
+ * copies of metadata entities. The default {@link Object#clone()}
implementation is
+ * sufficient in most cases.
+ *
+ * @return A <strong>shallow</strong> copy of this metadata.
+ * @throws CloneNotSupportedException if the clone is not supported.
+ */
+ @Override
+ protected ModifiableMetadata clone() throws CloneNotSupportedException {
+ return (ModifiableMetadata) super.clone();
+ }
}
Modified:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -117,6 +117,11 @@ final class PropertyAccessor {
}
/**
+ * The standard which define the {@link #type} interface.
+ */
+ private final Citation standard;
+
+ /**
* The implemented metadata interface.
*/
final Class<?> type;
@@ -213,14 +218,16 @@ final class PropertyAccessor {
/**
* Creates a new property accessor for the specified metadata
implementation.
*
- * @param implementation The type of metadata implementations to wrap.
+ * @param standard The standard which define the {@code type} interface.
* @param type The interface implemented by the metadata, which must be
* the value returned by {@link #getStandardType(Class, String)}.
+ * @param implementation The class of metadata implementations.
*/
- PropertyAccessor(final Class<?> implementation, final Class<?> type) {
+ PropertyAccessor(final Citation standard, final Class<?> type, final
Class<?> implementation) {
assert type.isAssignableFrom(implementation) : implementation;
- this.implementation = implementation;
+ this.standard = standard;
this.type = type;
+ this.implementation = implementation;
this.getters = getGetters(type);
int allCount = getters.length;
int standardCount = allCount;
@@ -703,7 +710,7 @@ final class PropertyAccessor {
ParameterDescriptor<?> descriptor = descriptors[index];
if (descriptor == null) {
final Class<?> elementType = elementTypes[index];
- final Citation standard = null; // TODO
+ final Citation standard = this.standard;
final String name = name(index,
KeyNamePolicy.UML_IDENTIFIER);
final Method getter = getters[index];
ValueRange range = null;
@@ -958,8 +965,8 @@ final class PropertyAccessor {
* runtime. However other implementations could use
unchecked collection.
* There is not much we can do...
*/
- final Collection<Object> unsafe = (Collection<Object>)
addTo;
- unsafe.add(elements[0]);
+ // No @SuppressWarnings because this is a real hole.
+ ((Collection<Object>) addTo).add(elements[0]);
}
}
/*
@@ -1104,12 +1111,13 @@ final class PropertyAccessor {
assert implementation.isInstance(metadata) : metadata;
if (setters != null) {
final Object[] arguments = new Object[1];
+ final Cloner cloner = new Cloner();
for (int i=0; i<allCount; i++) {
final Method setter = setters[i];
if (setter != null) {
final Method getter = getters[i];
final Object source = get(getter, metadata);
- final Object target = null; // TODO
ModifiableMetadata.unmodifiable(source);
+ final Object target = cloner.clone(source);
if (source != target) {
arguments[0] = target;
set(setter, metadata, arguments);
Modified:
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyDescriptor.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -54,6 +54,11 @@ import java.util.Objects;
* @module
*
* @see MetadataStandard#asDescriptorMap(Object, KeyNamePolicy,
NullValuePolicy)
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-80">SIS-80</a>
+ *
+ * @todo Implementing {@code ParameterDescriptor} is not really appropriate
since metadata properties
+ * are not parameters. Implementing {@link
org.opengis.feature.type.PropertyDescriptor} would
+ * be better, but the later is not yet part of GeoAPI standard and needs
cleaning. See SIS-80.
*/
@Immutable
class PropertyDescriptor<T> extends SimpleReferenceIdentifier implements
ParameterDescriptor<T> {
Modified:
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyDescriptorTest.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -24,7 +24,7 @@ import org.apache.sis.internal.simple.Si
import org.apache.sis.test.TestCase;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
/**
@@ -92,4 +92,14 @@ public final strictfp class PropertyDesc
assertEquals(0, descriptor.getMinimumOccurs());
assertEquals(Integer.MAX_VALUE, descriptor.getMaximumOccurs());
}
+
+ /**
+ * Tests serialization.
+ *
+ * @throws NoSuchMethodException Should never happen.
+ */
+ @Test
+ public void testSerialization() throws NoSuchMethodException {
+ assertSerializedEquals(create(InternationalString.class, "getTitle",
"title"));
+ }
}
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/simple/SimpleCitation.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -28,9 +28,11 @@ import org.opengis.metadata.citation.Pre
import org.opengis.metadata.citation.ResponsibleParty;
import org.opengis.metadata.citation.Series;
import org.opengis.util.InternationalString;
-
import org.apache.sis.util.iso.SimpleInternationalString;
+// Related to JDK7
+import java.util.Objects;
+
/**
* A trivial implementation of {@link Citation}.
@@ -87,6 +89,28 @@ public class SimpleCitation implements C
@Override public String getISSN()
{return null;}
/**
+ * Compares the given object with this citation for equality.
+ *
+ * @param obj The object to compare with this citation.
+ * @return {@code true} if both objects are equal.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj != null && obj.getClass() == getClass()) {
+ return Objects.equals(title, ((SimpleCitation) obj).title);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for this citation.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(title) ^ (int) serialVersionUID;
+ }
+
+ /**
* Returns a string representation of this citation.
*/
@Override
Modified:
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java?rev=1451422&r1=1451421&r2=1451422&view=diff
==============================================================================
---
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
[UTF-8] (original)
+++
sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
[UTF-8] Thu Feb 28 23:09:22 2013
@@ -18,6 +18,7 @@ package org.apache.sis.internal.util;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
+import net.jcip.annotations.NotThreadSafe;
import org.apache.sis.util.Workaround;
import org.apache.sis.util.resources.Errors;
@@ -31,15 +32,18 @@ import org.apache.sis.util.resources.Err
* @version 0.3
* @module
*/
+@NotThreadSafe
@Workaround(library="JDK", version="1.7")
-public final class Cloner {
+public class Cloner {
/**
* The type of the object to clone, or {@code null} if not yet specified.
+ * Used for checking if the cached {@linkplain #method} is still valid.
*/
private Class<?> type;
/**
* The {@code clone()} method, or {@code null} if not yet determined.
+ * This is cached for better performance if many instances of the same
type are cloned.
*/
private Method method;
@@ -50,7 +54,30 @@ public final class Cloner {
}
/**
- * Clones the given object.
+ * Invoked when the given object can not be cloned because no public
{@code clone()} method
+ * has been found. If this method returns {@code true}, then the {@link
#clone(Object)}
+ * method in this class will throw a {@link CloneNotSupportedException}.
Otherwise the
+ * {@code clone(Object)} method will return the original object.
+ *
+ * <p>The default implementation returns {@code true} in every cases.
+ * Subclasses can override this method if they need a different
behavior.</p>
+ *
+ * @param object The object that can not be cloned.
+ * @return {@code true} if the problem shall be considered a clone failure.
+ */
+ protected boolean isCloneRequired(final Object object) {
+ return true;
+ }
+
+ /**
+ * Clones the given object. If the given object does not provide a public
{@code clone()}
+ * method, then there is a choice:
+ *
+ * <ul>
+ * <li>If {@code isCloneRequired(object)} returns {@code true} (the
default),
+ * then a {@link CloneNotSupportedException} is thrown.</li>
+ * <li>Otherwise the given object is returned.</li>
+ * </ul>
*
* @param object The object to clone, or {@code null}.
* @return A clone of the given object, or {@code null} if {@code object}
was null.
@@ -66,7 +93,9 @@ public final class Cloner {
method = valueType.getMethod("clone", (Class<?>[]) null);
type = valueType; // Set only if the above line succeed.
}
- return method.invoke(object, (Object[]) null);
+ if (method != null) { // May be null if previous call threw
NoSuchMethodException.
+ return method.invoke(object, (Object[]) null);
+ }
} catch (InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof CloneNotSupportedException) {
@@ -79,9 +108,16 @@ public final class Cloner {
throw (Error) cause;
}
throw fail(e);
+ } catch (NoSuchMethodException e) {
+ if (isCloneRequired(object)) {
+ throw fail(e);
+ }
+ method = null;
+ type = valueType;
} catch (ReflectiveOperationException e) {
throw fail(e);
}
+ return object;
}
/**
@@ -91,7 +127,7 @@ public final class Cloner {
* @param cause The cause for the failure to clone an object.
* @return An exception with an error message and the given cause.
*/
- public CloneNotSupportedException fail(final Throwable cause) {
+ private CloneNotSupportedException fail(final Throwable cause) {
CloneNotSupportedException e = new CloneNotSupportedException(
Errors.format(Errors.Keys.CloneNotSupported_1, type));
e.initCause(cause);