sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1398638 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/util/resources/ main/java/org/apache/sis/xml/ test/java/org/apache/sis/test/suite/ test/java/org/apache/sis/xml/
Date Tue, 16 Oct 2012 04:13:02 GMT
Author: desruisseaux
Date: Tue Oct 16 04:13:01 2012
New Revision: 1398638

URL: http://svn.apache.org/viewvc?rev=1398638&view=rev
Log:
Added XLink implementation.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java   (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1398638&r1=1398637&r2=1398638&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java Tue Oct 16 04:13:01 2012
@@ -51,6 +51,11 @@ public final class Errors extends Indexe
         public static final int EmptyArgument_1 = 1;
 
         /**
+         * Attribute “{0}” is not allowed for an object of type ‘{1}’.
+         */
+        public static final int ForbiddenAttribute_2 = 21;
+
+        /**
          * Argument ‘{0}’ can not be an instance of ‘{1}’.
          */
         public static final int IllegalArgumentClass_2 = 17;
@@ -92,6 +97,11 @@ public final class Errors extends Indexe
         public static final int IllegalRange_2 = 11;
 
         /**
+         * Value “{1}” of attribute ‘{0}’ is inconsistent with other attributes.
+         */
+        public static final int InconsistentAttribute_2 = 27;
+
+        /**
          * Index {0} is out of bounds.
          */
         public static final int IndexOutOfBounds_1 = 4;
@@ -102,6 +112,11 @@ public final class Errors extends Indexe
         public static final int KeyCollision_1 = 19;
 
         /**
+         * Attribute “{0}” is mandatory for an object of type ‘{1}’.
+         */
+        public static final int MandatoryAttribute_2 = 22;
+
+        /**
          * Argument ‘{0}’ shall not be negative. The given value was {1}.
          */
         public static final int NegativeArgument_2 = 8;
@@ -132,6 +147,26 @@ public final class Errors extends Indexe
         public static final int UnexpectedArgumentDimension_3 = 5;
 
         /**
+         * This affine transform is unmodifiable.
+         */
+        public static final int UnmodifiableAffineTransform = 23;
+
+        /**
+         * This geometry is unmodifiable.
+         */
+        public static final int UnmodifiableGeometry = 24;
+
+        /**
+         * This metadata is unmodifiable.
+         */
+        public static final int UnmodifiableMetadata = 25;
+
+        /**
+         * Object ‘{0}’ is unmodifiable.
+         */
+        public static final int UnmodifiableObject_1 = 26;
+
+        /**
          * The ‘{0}’ operation is unsupported.
          */
         public static final int UnsupportedOperation_1 = 20;

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1398638&r1=1398637&r2=1398638&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties Tue Oct 16 04:13:01 2012
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 EmptyArgument_1                 = Argument \u2018{0}\u2019 shall not be empty.
+ForbiddenAttribute_2            = Attribute \u201c{0}\u201d is not allowed for an object of type \u2018{1}\u2019.
 IllegalArgument_1               = Illegal value for argument \u2018{0}\u2019.
 IllegalArgumentClass_2          = Argument \u2018{0}\u2019 can not be an instance of \u2018{1}\u2019.
 IllegalArgumentClass_3          = Argument \u2018{0}\u2019 can not be an instance of \u2018{1}\u2019. Expected an instance of \u2018{2}\u2019 or derived type.
@@ -23,14 +24,20 @@ IllegalBitsPattern_1            = Illega
 IllegalClass_2                  = Class \u2018{0}\u2019 is illegal. It must be \u2018{1}\u2019 or a derived class.
 IllegalLanguageCode_1           = The \u201c{0}\u201d language is not recognized.
 IllegalRange_2                  = Range [{0} \u2026 {1}] is not valid.
+InconsistentAttribute_2         = Value \u201c{1}\u201d of attribute \u2018{0}\u2019 is inconsistent with other attributes.
 IndexOutOfBounds_1              = Index {0} is out of bounds.
 KeyCollision_1                  = A different value is already associated to the \u201c{0}\u201d key.
+MandatoryAttribute_2            = Attribute \u201c{0}\u201d is mandatory for an object of type \u2018{1}\u2019.
 NegativeArgument_2              = Argument \u2018{0}\u2019 shall not be negative. The given value was {1}.
 NotANumber_1                    = Argument \u2018{0}\u2019 shall not be NaN (Not-a-Number).
 NotAPrimitiveWrapper_1          = Class \u2018{0}\u2019 is not a primitive type wrapper.
 NullArgument_1                  = Argument \u2018{0}\u2019 shall not be null.
 RecursiveCreateCallForKey_1     = Recursive call while creating an object for the \u201c{0}\u201d key.
 UnexpectedArgumentDimension_3   = Argument \u2018{0}\u2019 has {1} dimensions, while {2} was expected.
+UnmodifiableAffineTransform     = This affine transform is unmodifiable.
+UnmodifiableGeometry            = This geometry is unmodifiable.
+UnmodifiableMetadata            = This metadata is unmodifiable.
+UnmodifiableObject_1            = Object \u2018{0}\u2019 is unmodifiable.
 UnsupportedOperation_1          = The \u2018{0}\u2019 operation is unsupported.
 ValueAlreadyDefined_1           = A value is already defined for \u201c{0}\u201d.
 ValueNotGreaterThanZero_2       = Value \u2018{0}\u2019={1} is invalid. Expected a number greater than 0.

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1398638&r1=1398637&r2=1398638&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties Tue Oct 16 04:13:01 2012
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 EmptyArgument_1                 = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre vide.
+ForbiddenAttribute_2            = L\u2019attribut \u201c{0}\u201d n\u2019est pas autoris\u00e9 pour un objet de type \u2018{1}\u2019.
 IllegalArgument_1               = Valeur ill\u00e9gale pour l\u2019argument \u2018{0}\u2019.
 IllegalArgumentClass_2          = L\u2019argument \u2018{0}\u2019 ne peut pas \u00eatre de type \u2018{1}\u2019.
 IllegalArgumentClass_3          = L\u2019argument \u2018{0}\u2019 ne peut pas \u00eatre de type \u2018{1}\u2019. Une instance de \u2018{2}\u2019 ou d\u2019un type d\u00e9riv\u00e9 \u00e9tait attendue.
@@ -23,14 +24,20 @@ IllegalBitsPattern_1            = Patter
 IllegalClass_2                  = La classe \u2018{0}\u2019 est ill\u00e9gale. Il doit s\u2019agir d\u2019une classe \u2018{1}\u2019 ou d\u00e9riv\u00e9e.
 IllegalLanguageCode_1           = Le code de langue \u201c{0}\u201d n\u2019est pas reconnu.
 IllegalRange_2                  = La plage [{0} \u2026 {1}] n\u2019est pas valide.
+InconsistentAttribute_2         = La valeur \u201c{1}\u201d de l\u2019attribut \u2018{0}\u2019 n\u2019est pas coh\u00e9rente avec celles des autres attributs.
 IndexOutOfBounds_1              = L\u2019index {0} est en dehors des limites permises.
 KeyCollision_1                  = Une valeur diff\u00e9rente est d\u00e9j\u00e0 associ\u00e9e \u00e0 la cl\u00e9 \u201c{0}\u201d.
+MandatoryAttribute_2            = L\u2019attribut \u201c{0}\u201d est obligatoire pour un objet de type \u2018{1}\u2019.
 NegativeArgument_2              = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre n\u00e9gatif. La valeur donn\u00e9e \u00e9tait {1}.
 NotANumber_1                    = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre NaN (Not-a-Number).
 NotAPrimitiveWrapper_1          = La classe \u2018{0}\u2019 n\u2019est pas un adaptateur d\u2019un type primitif.
 NullArgument_1                  = L\u2019argument \u2018{0}\u2019 ne doit pas \u00eatre nul.
 RecursiveCreateCallForKey_1     = Appel r\u00e9cursif lors de la cr\u00e9ation d\u2019un objet pour la cl\u00e9 \u201c{0}\u201d.
 UnexpectedArgumentDimension_3   = L\u2019argument \u2018{0}\u2019 a {1} dimensions, alors qu\u2019on en attendait {2}.
+UnmodifiableAffineTransform     = Cette transformation affine n\u2019est pas modifiable.
+UnmodifiableGeometry            = Cette g\u00e9om\u00e9trie n\u2019est pas modifiable.
+UnmodifiableMetadata            = Cette m\u00e9ta-donn\u00e9e n\u2019est pas modifiable.
+UnmodifiableObject_1            = L\u2019objet \u2018{0}\u2019 n\u2019est pas modifiable.
 UnsupportedOperation_1          = L\u2019op\u00e9ration \u2018{0}\u2019 n\u2019est pas support\u00e9e.
 ValueAlreadyDefined_1           = Une valeur est d\u00e9j\u00e0 d\u00e9finie pour \u201c{0}\u201d.
 ValueNotGreaterThanZero_2       = La valeur \u2018{0}\u2019={1} n\u2019est pas valide. On attendait un nombre positif non-nul.

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java?rev=1398638&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java Tue Oct 16 04:13:01 2012
@@ -0,0 +1,898 @@
+/*
+ * 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.xml;
+
+import java.net.URI;
+import java.util.Locale;
+import java.util.Objects;
+import java.io.Serializable;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlTransient;
+import net.jcip.annotations.ThreadSafe;
+
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.Classes;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * The XML attributes defined by OGC in the
+ * <a href="http://schemas.opengis.net/xlink/1.0.0/xlinks.xsd">xlink</a> schema.
+ *
+ * The allowed combinations of any one attribute depend on the value of the special
+ * {@link #getType() type} attribute. Following is a summary of the element types
+ * (columns) on which the global attributes (rows) are allowed, with an indication
+ * of whether a value is required (R) or optional (O)
+ * (Source: <a href="http://www.w3.org/TR/xlink/">W3C</a>):
+ *
+ * <blockquote><table class="sis">
+ * <tr>
+ *   <th>&nbsp;</th>
+ *   <th width="14%">{@link XLink.Type#SIMPLE simple}</th>
+ *   <th width="14%">{@link XLink.Type#EXTENDED extended}</th>
+ *   <th width="14%">{@link XLink.Type#LOCATOR locator}</th>
+ *   <th width="14%">{@link XLink.Type#ARC arc}</th>
+ *   <th width="14%">{@link XLink.Type#RESOURCE resource}</th>
+ *   <th width="14%">{@link XLink.Type#TITLE title}</th>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getType() type}</th>
+ *   <td>R</td><td>R</td><td>R</td><td>R</td><td>R</td><td>R</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getHRef() href}</th>
+ *   <td>O</td><td>&nbsp;</td><td>R</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getRole() role}</th>
+ *   <td>O</td><td>O</td><td>O</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getArcRole() arcrole}</th>
+ *   <td>O</td><td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getTitle() title}</th>
+ *   <td>O</td><td>O</td><td>O</td><td>O</td><td>O</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getShow() show}</th>
+ *   <td>O</td><td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getActuate() actuate}</th>
+ *   <td>O</td><td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getLabel() label}</th>
+ *   <td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getFrom() from}</th>
+ *   <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr>
+ *   <tr align="center"><th align="left">&nbsp;{@link #getTo() to}</th>
+ *   <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>O</td><td>&nbsp;</td><td>&nbsp;</td>
+ * </tr></table></blockquote>
+ *
+ * When {@code xlink} attributes are found at unmarshalling time instead of an object definition,
+ * those attributes are given to the {@link ObjectLinker#resolve(Class, XLink)} method. Users can
+ * override that method in order to fetch an instance in some catalog for the given {@code xlink}
+ * values.
+ *
+ * @author  Guilhem Legal (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.00)
+ * @version 0.3
+ * @module
+ *
+ * @see <a href="http://www.w3.org/TR/xlink/">XML Linking Language</a>
+ * @see <a href="http://schemas.opengis.net/xlink/1.0.0/xlinks.xsd">OGC schema</a>
+ */
+@ThreadSafe
+@XmlTransient
+public class XLink implements Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4349950135677857725L;
+
+    /**
+     * The type of link. If {@code null}, then the type will be inferred by {@link #getType()}.
+     *
+     * @see #getType()
+     */
+    private Type type;
+
+    /**
+     * A URN to an external resources, or to an other part of a XML document, or an identifier.
+     *
+     * @see #getHRef()
+     * @category locator
+     */
+    private URI href;
+
+    /**
+     * A URI reference for some description of the arc role.
+     *
+     * @see #getRole()
+     * @category semantic
+     */
+    private URI role;
+
+    /**
+     * A URI reference for some description of the arc role.
+     *
+     * @see #getArcRole()
+     * @category semantic
+     */
+    private URI arcrole;
+
+    /**
+     * Just as with resources, this is simply a human-readable string with a short description
+     * for the arc.
+     *
+     * @see #getTitle()
+     * @category semantic
+     */
+    private InternationalString title;
+
+    /**
+     * Communicates the desired presentation of the ending resource on traversal
+     * from the starting resource.
+     *
+     * @see #getShow()
+     * @category behavior
+     */
+    private Show show;
+
+    /**
+     * Communicates the desired timing of traversal from the starting resource to the ending resource.
+     *
+     * @see #getActuate()
+     * @category behavior
+     */
+    private Actuate actuate;
+
+    /**
+     * Identifies the target of a {@code from} or {@code to} attribute.
+     *
+     * @see #getLabel()
+     * @category traversal
+     */
+    private String label;
+
+    /**
+     * The starting resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @see #getFrom()
+     * @category traversal
+     */
+    private String from;
+
+    /**
+     * The ending resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @see #getTo()
+     * @category traversal
+     */
+    private String to;
+
+    /**
+     * The cached hash code value, computed only if this {@code XLink} is unmodifiable. Otherwise,
+     * this field is left to zero. This field is computed when the {@link #freeze()} method has
+     * been invoked.
+     */
+    private int hashCode;
+
+    /**
+     * Creates a new link. The initial value of all attributes is {@code null}.
+     */
+    public XLink() {
+    }
+
+    /**
+     * Creates a new link as a copy of the given link.
+     *
+     * @param link The link to copy, or {@code null} if none.
+     */
+    public XLink(final XLink link) {
+        if (link != null) {
+            type    = link.type;
+            href    = link.href;
+            role    = link.role;
+            arcrole = link.arcrole;
+            title   = link.title;
+            show    = link.show;
+            actuate = link.actuate;
+            label   = link.label;
+            from    = link.from;
+            to      = link.to;
+        }
+    }
+
+    /**
+     * The type of a {@code xlink}. This type can be determined from the set of non-null
+     * attribute values in a {@link XLink} instance.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-3.18)
+     * @version 0.3
+     * @module
+     *
+     * @see XLink#getType()
+     */
+    @XmlEnum
+    public static enum Type {
+        /**
+         * A simple link. Allows the {@link XLink#getHRef() href}, {@link XLink#getRole() role},
+         * {@link XLink#getArcRole() arcrole}, {@link #getTitle() title}, {@link XLink#getShow()
+         * show} and {@link XLink#getActuate() actuate} attributes, all of them being optional.
+         */
+        @XmlEnumValue("simple")
+        SIMPLE(0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40, 0x1),
+
+        /**
+         * An extended, possibly multi-resource, link. Allows the {@link XLink#getRole() role}
+         * and {@link #getTitle() title} attributes, all of them being optional.
+         */
+        @XmlEnumValue("extended")
+        EXTENDED(0x1 | 0x4 | 0x10, 0x1),
+
+        /**
+         * A pointer to an external resource. Allows the {@link XLink#getHRef() href},
+         * {@link XLink#getRole() role}, {@link #getTitle() title} and {@link XLink#getLabel()
+         * label} attributes, where {@code href} is mandatory and all other are optional.
+         */
+        @XmlEnumValue("locator")
+        LOCATOR(0x1 | 0x2 | 0x4 | 0x10 | 0x80, 0x1 | 0x2),
+
+        /**
+         * An internal resource. Allows the {@link XLink#getRole() role},  {@link #getTitle() title}
+         * and {@link #getLabel() label} attributes, all of them being optional.
+         */
+        @XmlEnumValue("resource")
+        RESOURCE(0x1 | 0x4 | 0x10 | 0x80, 0x1),
+
+        /**
+         * A traversal rule between resources. Allows the {@link XLink#getArcRole() arcrole},
+         * {@link #getTitle() title}, {@link XLink#getShow() show}, {@link XLink#getActuate()
+         * actuate} {@link #getFrom() from} and {@link #getTo() to} attributes, all of them
+         * being optional.
+         */
+        @XmlEnumValue("arc")
+        ARC(0x1 | 0x8 | 0x10 | 0x20 | 0x40 | 0x100 | 0x200, 0x1),
+
+        /**
+         * A descriptive title for another linking element.
+         */
+        @XmlEnumValue("title")
+        TITLE(0x1, 0x1),
+
+        /**
+         * A special value for computing the type automatically from the {@link XLink} attributes.
+         * After a call to {@code XLink.setType(AUTO)}, any call to {@code XLink.getType()} will
+         * infer the type from the non-null attributes as according the table documented in the
+         * {@link XLink} javadoc.
+         */
+        AUTO(-1, 0);
+
+        /**
+         * A bitmask which specified the non-null fields expected for a given type.
+         * The bit values are:
+         * <ul>
+         *   <li>{@code type}:     0x1</li>
+         *   <li>{@code href}:     0x2</li>
+         *   <li>{@code role}:     0x4</li>
+         *   <li>{@code arcrole}:  0x8</li>
+         *   <li>{@code title}:   0x10</li>
+         *   <li>{@code show}:    0x20</li>
+         *   <li>{@code actuate}: 0x40</li>
+         *   <li>{@code label}:   0x80</li>
+         *   <li>{@code from}:   0x100</li>
+         *   <li>{@code to}:     0x200</li>
+         * </ul>
+         */
+        final int fieldMask, mandatory;
+
+        /**
+         * Creates a new type which allows the fields specified by the given mask.
+         */
+        private Type(final int mask, final int mandatory) {
+            this.fieldMask = mask;
+            this.mandatory = mandatory;
+        }
+
+        /**
+         * Returns the attribute name for this type.
+         */
+        final String identifier() {
+            return name().toLowerCase(Locale.US);
+        }
+    }
+
+    /**
+     * Returns a mask of fields for which a non-null value has been defined.
+     * The bit values are defined in the {@link XLink.Type#fieldMask} javadoc.
+     */
+    private int fieldMask() {
+        int mask = 0;
+        if (type    != null) mask |= 0x1;
+        if (href    != null) mask |= 0x2;
+        if (role    != null) mask |= 0x4;
+        if (arcrole != null) mask |= 0x8;
+        if (title   != null) mask |= 0x10;
+        if (show    != null) mask |= 0x20;
+        if (actuate != null) mask |= 0x40;
+        if (label   != null) mask |= 0x80;
+        if (from    != null) mask |= 0x100;
+        if (to      != null) mask |= 0x200;
+        return mask;
+    }
+
+    /**
+     * Returns the type of link. May have one of the following values:
+     *
+     * <ul>
+     *   <li><b>simple:</b>   a simple link</li>
+     *   <li><b>extended:</b> an extended, possibly multi-resource, link</li>
+     *   <li><b>locator:</b>  a pointer to an external resource</li>
+     *   <li><b>resource:</b> an internal resource</li>
+     *   <li><b>arc:</b>      a traversal rule between resources</li>
+     *   <li><b>title:</b>    a descriptive title for another linking element</li>
+     * </ul>
+     *
+     * <p>The default value is {@code null}. If the {@link #setType(XLink.Type)} method has been
+     * invoked with the {@link org.apache.sis.xml.XLink.Type#AUTO AUTO} enum, then this method
+     * will infer a type from the attributes having a non-null value.</p>
+     *
+     * @return The type of link, or {@code null}.
+     */
+    @XmlAttribute(name = "type", namespace = Namespaces.XLINK, required = true)
+    public synchronized Type getType() {
+        if (type != Type.AUTO) {
+            return type;
+        }
+        Type best = null;
+        int min = Integer.SIZE;
+        final int defined = fieldMask();
+        final int undefined = ~(defined | 0x1);
+        for (final Type candidate : Type.values()) {
+            final int forbidden = ~candidate.fieldMask;
+            if (forbidden == 0) {
+                continue; // Skip the AUTO enum.
+            }
+            // Test if this XLink instance defines only values allowed by the candidate type.
+            if ((defined & forbidden) != 0) {
+                continue;
+            }
+            // Test if this XLink instance defines all mandatory fields.
+            if ((undefined & candidate.mandatory) != 0) {
+                continue;
+            }
+            // Select the type requerying the smallest amount of fields.
+            final int n = Integer.bitCount(undefined & candidate.fieldMask);
+            if (n < min) {
+                min = n;
+                best = candidate;
+            }
+        }
+        return best; // May still null.
+    }
+
+    /**
+     * Sets the type of link. Any value different than {@link org.apache.sis.xml.XLink.Type#AUTO
+     * Type.AUTO} (including {@code null}) will overwrite the value inferred automatically by
+     * {@link #getType()}. A {@code AUTO} value will enable automatic type detection.
+     *
+     * @param type The new type of link, or {@code null} if none.
+     */
+    public synchronized void setType(final Type type) {
+        canWrite(0x1, "type", "type"); // We want a non-null value in all cases.
+        if (type != null && (fieldMask() & ~type.fieldMask) != 0) {
+            throw new IllegalStateException(Errors.format(Errors.Keys.InconsistentAttribute_2, "type", type.identifier()));
+        }
+        this.type = type;
+    }
+
+    /**
+     * Checks if the given attribute can be set.
+     *
+     * @param  field The attribute code, as documented in {@link XLink.Type#fieldMask}.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the given field can not be set for this kind of {@code xlink}.
+     */
+    private void canWrite(final int field, final String name, final Object value) throws IllegalStateException {
+        if (hashCode != 0) {
+            throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, "XLink"));
+        }
+        final Type type = this.type;
+        if (type != null) {
+            if (value != null) {
+                if ((type.fieldMask & field) == 0) {
+                    throw new IllegalStateException(Errors.format(
+                            Errors.Keys.ForbiddenAttribute_2, name, type.identifier()));
+                }
+            } else {
+                if ((type.mandatory & field) != 0) {
+                    throw new IllegalStateException(Errors.format(
+                            Errors.Keys.MandatoryAttribute_2, name, type.identifier()));
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a URN to an external resources, or to an other part of a XML document, or an
+     * identifier.
+     *
+     * {@note This serves a role similar to <code>idref</code>. The <code>idref</code>
+     * attribute allows an XML element to refer to another XML element that has a
+     * corresponding <code>id</code> attribute.}
+     *
+     * @return A URN to a resources, or {@code null} if none.
+     *
+     * @category locator
+     */
+    @XmlAttribute(name = "href", namespace = Namespaces.XLINK)
+    public synchronized URI getHRef() {
+        return href;
+    }
+
+    /**
+     * Sets the URN to a resources.
+     *
+     * @param  href A URN to a resources, or {@code null} if none.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "href"} attribute.
+     *
+     * @category locator
+     */
+    public synchronized void setHRef(final URI href) throws IllegalStateException {
+        canWrite(0x2, "href", href);
+        this.href = href;
+    }
+
+    /**
+     * Returns a URI reference for some description of the arc role.
+     *
+     * @return A URI reference for some description of the arc role, or {@code null} if none.
+     *
+     * @category semantic
+     */
+    @XmlAttribute(name = "role", namespace = Namespaces.XLINK)
+    public synchronized URI getRole() {
+        return role;
+    }
+
+    /**
+     * Sets the URI reference for some description of the arc role.
+     *
+     * @param  role A URI reference for some description of the arc role, or {@code null} if none.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "role"} attribute.
+     *
+     * @category semantic
+     */
+    public synchronized void setRole(final URI role) throws IllegalStateException {
+        canWrite(0x4, "role", role);
+        this.role = role;
+    }
+
+    /**
+     * Returns a URI reference for some description of the arc role.
+     *
+     * @return A URI reference for some description of the arc role, or {@code null} if none.
+     *
+     * @category semantic
+     */
+    @XmlAttribute(name = "arcrole", namespace = Namespaces.XLINK)
+    public synchronized URI getArcRole() {
+        return arcrole;
+    }
+
+    /**
+     * Sets a URI reference for some description of the arc role.
+     *
+     * @param  arcrole A URI reference for some description of the arc role, or {@code null} if none.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "arcrole"} attribute.
+     *
+     * @category semantic
+     */
+    public synchronized void setArcRole(final URI arcrole) throws IllegalStateException {
+        canWrite(0x8, "arcrole", arcrole);
+        this.arcrole = arcrole;
+    }
+
+    /**
+     * Returns a human-readable string with a short description for the arc.
+     *
+     * @return A human-readable string with a short description for the arc, or {@code null} if none.
+     *
+     * @category semantic
+     */
+    @XmlAttribute(name = "title", namespace = Namespaces.XLINK)
+    public synchronized InternationalString getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets a human-readable string with a short description for the arc.
+     *
+     * @param  title A human-readable string with a short description for the arc,
+     *         or {@code null} if none.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "title"} attribute.
+     *
+     * @category semantic
+     */
+    public synchronized void setTitle(final InternationalString title) throws IllegalStateException {
+        canWrite(0x10, "title", title);
+        this.title = title;
+    }
+
+    /**
+     * Communicates the desired presentation of the ending resource on traversal
+     * from the starting resource.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-3.18)
+     * @version 0.3
+     * @module
+     *
+     * @see XLink#getShow()
+     */
+    @XmlEnum
+    public static enum Show {
+        /**
+         * Load ending resource in a new window, frame, pane, or other presentation context.
+         */
+        @XmlEnumValue("new") NEW,
+
+        /**
+         * Load the resource in the same window, frame, pane, or other presentation context.
+         */
+        @XmlEnumValue("replace") REPLACE,
+
+        /**
+         * Load ending resource in place of the presentation of the starting resource.
+         */
+        @XmlEnumValue("embed") EMBED,
+
+        /**
+         * Behavior is unconstrained; examine other markup in the link for hints.
+         */
+        @XmlEnumValue("other") OTHER,
+
+        /**
+         * Behavior is unconstrained.
+         */
+        @XmlEnumValue("none") NONE
+    }
+
+    /**
+     * Returns the desired presentation of the ending resource on traversal
+     * from the starting resource. It's value should be treated as follows:
+     * <p>
+     * <ul>
+     *   <li><b>new:</b>     load ending resource in a new window, frame, pane, or other presentation context</li>
+     *   <li><b>replace:</b> load the resource in the same window, frame, pane, or other presentation context</li>
+     *   <li><b>embed:</b>   load ending resource in place of the presentation of the starting resource</li>
+     *   <li><b>other:</b>   behavior is unconstrained; examine other markup in the link for hints</li>
+     *   <li><b>none:</b>    behavior is unconstrained</li>
+     * </ul>
+     *
+     * @return The desired presentation of the ending resource, or {@code null} if unspecified.
+     *
+     * @category behavior
+     */
+    @XmlAttribute(name = "show", namespace = Namespaces.XLINK)
+    public synchronized Show getShow() {
+        return show;
+    }
+
+    /**
+     * Sets the desired presentation of the ending resource on traversal from the starting resource.
+     *
+     * @param  show The desired presentation of the ending resource, or {@code null} if unspecified.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "show"} attribute.
+     *
+     * @category behavior
+     */
+    public synchronized void setShow(final Show show) throws IllegalStateException {
+        canWrite(0x20, "show", show);
+        this.show = show;
+    }
+
+    /**
+     * Communicates the desired timing of traversal from the starting resource to the ending
+     * resource.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-3.18)
+     * @version 0.3
+     * @module
+     *
+     * @see XLink#getActuate()
+     */
+    @XmlEnum
+    public static enum Actuate {
+        /**
+         * Traverse to the ending resource immediately on loading the starting resource.
+         */
+        @XmlEnumValue("onLoad") ON_LOAD,
+
+        /**
+         * Traverse from the starting resource to the ending resource only on a post-loading event
+         * triggered for this purpose.
+         */
+        @XmlEnumValue("onRequest") ON_REQUEST,
+
+        /**
+         * Behavior is unconstrained; examine other markup in link for hints.
+         */
+        @XmlEnumValue("other") OTHER,
+
+        /**
+         * Behavior is unconstrained.
+         */
+        @XmlEnumValue("none") NONE
+    }
+
+    /**
+     * Returns the desired timing of traversal from the starting resource to the ending
+     * resource. It's value should be treated as follows:
+     * <p>
+     * <ul>
+     *   <li><b>onLoad:</b>    traverse to the ending resource immediately on loading the starting resource</li>
+     *   <li><b>onRequest:</b> traverse from the starting resource to the ending resource only on a post-loading event triggered for this purpose</li>
+     *   <li><b>other:</b>     behavior is unconstrained; examine other markup in link for hints</li>
+     *   <li><b>none:</b>      behavior is unconstrained</li>
+     * </ul>
+     *
+     * @return The desired timing of traversal from the starting resource to the ending resource,
+     *         or {@code null} if unspecified.
+     *
+     * @category behavior
+     */
+    @XmlAttribute(name = "actuate", namespace = Namespaces.XLINK)
+    public synchronized Actuate getActuate() {
+        return actuate;
+    }
+
+    /**
+     * Sets the desired timing of traversal from the starting resource to the ending resource.
+     *
+     * @param  actuate The desired timing of traversal from the starting resource to the ending
+     *         resource, or {@code null} if unspecified.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "actuate"} attribute.
+     *
+     * @category behavior
+     */
+    public synchronized void setActuate(final Actuate actuate) throws IllegalStateException {
+        canWrite(0x40, "actuate", actuate);
+        this.actuate = actuate;
+    }
+
+    /**
+     * Returns an identification of the target of a {@code from} or {@code to} attribute.
+     *
+     * @return An identification of the target of a {@code from} or {@code to} attribute, or {@code null}.
+     *
+     * @category traversal
+     */
+    public synchronized String getLabel() {
+        return label;
+    }
+
+    /**
+     * Sets an identification of the target of a {@code from} or {@code to} attribute.
+     *
+     * @param  label An identification of the target of a {@code from} or {@code to} attribute, or {@code null}.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "label"} attribute.
+     *
+     * @category traversal
+     */
+    public synchronized void setLabel(final String label) throws IllegalStateException {
+        canWrite(0x80, "label", label);
+        this.label = label;
+    }
+
+    /**
+     * Returns the starting resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @return The starting resource, or {@code null}.
+     *
+     * @category traversal
+     */
+    public synchronized String getFrom() {
+        return from;
+    }
+
+    /**
+     * Sets the starting resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @param  from The starting resource, or {@code null}.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "from"} attribute.
+     *
+     * @category traversal
+     */
+    public synchronized void setFrom(final String from) throws IllegalStateException {
+        canWrite(0x100, "from", from);
+        this.from = from;
+    }
+
+    /**
+     * Returns the ending resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @return The ending resource, or {@code null}.
+     *
+     * @category traversal
+     */
+    public synchronized String getTo() {
+        return to;
+    }
+
+    /**
+     * Sets the ending resource. The value must correspond to the same value for some
+     * {@code label} attribute.
+     *
+     * @param  to The ending resource, or {@code null}.
+     * @throws UnsupportedOperationException If this {@code xlink} is unmodifiable.
+     * @throws IllegalStateException If the link type {@linkplain #setType has been explicitely set}.
+     *         and that type does not allow the {@code "to"} attribute.
+     *
+     * @category traversal
+     */
+    public synchronized void setTo(final String to) throws IllegalStateException {
+        canWrite(0x200, "to", to);
+        this.to = to;
+    }
+
+    /**
+     * Marks this {@code xlink} as unmodifiable. After this method call, any call to a setter
+     * method will throw an {@link UnsupportedOperationException}.
+     * <p>
+     * After the first call to this method, any subsequent calls have no effect.
+     *
+     * @todo We have a hole, since the {@link #getTitle() title} attribute could be modifiable...
+     */
+    public synchronized void freeze() {
+        if (hashCode == 0) {
+            hashCode = hash();
+        }
+    }
+
+    /**
+     * Compares this {@code XLink} with the given object for equality.
+     *
+     * @param object The object to compare with this XLink.
+     */
+    @Override
+    public synchronized boolean equals(final Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (object != null && object.getClass() == getClass()) {
+            final XLink that = (XLink) object;
+            final int h0 = hashCode;
+            if (h0 != 0) {
+                final int h1 = that.hashCode;
+                if (h1 != 0 && h0 != h1) {
+                    return false; // Slight optimization using the pre-computed hash code values.
+                }
+            }
+            return Objects.equals(this.type,    that.type)    &&
+                   Objects.equals(this.href,    that.href)    &&
+                   Objects.equals(this.role,    that.role)    &&
+                   Objects.equals(this.arcrole, that.arcrole) &&
+                   Objects.equals(this.title,   that.title)   &&
+                   Objects.equals(this.show,    that.show)    &&
+                   Objects.equals(this.actuate, that.actuate) &&
+                   Objects.equals(this.label,   that.label)   &&
+                   Objects.equals(this.from,    that.from)    &&
+                   Objects.equals(this.to,      that.to);
+        }
+        return false;
+    }
+
+    /**
+     * Returns a hash code value for this XLink.
+     */
+    @Override
+    public synchronized int hashCode() {
+        int hash = hashCode;
+        if (hash == 0) {
+            hash = hash();
+            // Do not save the hash code value, since it may change.
+        }
+        return hash;
+    }
+
+    /**
+     * Computes the hash code now. This method is guaranteed to return a value different
+     * than zero, in order to allow us to use 0 as a sentinel value for modifiable xlink.
+     */
+    private int hash() {
+        int hash = Objects.hash(href, role, arcrole, title, show, actuate, label, from, to, type) ^ (int) serialVersionUID;
+        if (hash == 0) {
+            hash = -1;
+        }
+        return hash;
+    }
+
+    /**
+     * Returns a string representation of this object. The default implementation returns the
+     * simple class name followed by non-null attributes, as in the example below:
+     *
+     * {@preformat text
+     *     XLink[type="locator", href="urn:ogc:def:method:EPSG::4326"]
+     * }
+     */
+    @Override
+    public synchronized String toString() {
+        final StringBuilder buffer = new StringBuilder(32);
+        buffer.append(Classes.getShortClassName(this)).append('[');
+        append(buffer, "type",    getType());
+        append(buffer, "href",    getHRef());
+        append(buffer, "role",    getRole());
+        append(buffer, "arcrole", getArcRole());
+        append(buffer, "title",   getTitle());
+        append(buffer, "show",    getShow());
+        append(buffer, "actuate", getActuate());
+        append(buffer, "label",   getLabel());
+        append(buffer, "from",    getFrom());
+        append(buffer, "to",      getTo());
+        return buffer.append(']').toString();
+    }
+
+    /**
+     * Appends the given attribute in the given buffer if the attribute value is not null.
+     * If the given value is an attribute, the XML name will be used rather than the Java
+     * field name.
+     */
+    private static void append(final StringBuilder buffer, final String label, Object value) {
+        if (value != null) {
+            if (buffer.charAt(buffer.length() - 1) != '[') {
+                buffer.append(", ");
+            }
+            if (value instanceof Enum<?>) try {
+                final XmlEnumValue xml = value.getClass().getField(((Enum<?>) value).name()).getAnnotation(XmlEnumValue.class);
+                if (xml != null) {
+                    value = xml.value();
+                }
+            } catch (NoSuchFieldException e) {
+                // Should never happen with Enums. But if it
+                // happen anyway, this is not a fatal error.
+                Logging.unexpectedException(XLink.class, "toString", e);
+            }
+            buffer.append(label).append("=\"").append(value).append('"');
+        }
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/xml/XLink.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1398638&r1=1398637&r2=1398638&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java Tue Oct 16 04:13:01 2012
@@ -48,7 +48,8 @@ import org.junit.runners.Suite;
   org.apache.sis.internal.util.ReferenceQueueConsumerTest.class,
   org.apache.sis.util.collection.WeakHashSetTest.class,
   org.apache.sis.util.collection.WeakValueHashMapTest.class,
-  org.apache.sis.util.collection.CacheTest.class
+  org.apache.sis.util.collection.CacheTest.class,
+  org.apache.sis.xml.XLinkTest.class
 })
 public final strictfp class UtilityTestSuite extends TestSuite {
 }

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java?rev=1398638&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java Tue Oct 16 04:13:01 2012
@@ -0,0 +1,190 @@
+/*
+ * 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.xml;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.apache.sis.util.type.SimpleInternationalString;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests the {@link XLink}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.18)
+ * @version 0.3
+ * @module
+ */
+public final strictfp class XLinkTest extends TestCase {
+    /**
+     * Tests the automatic {@link #getType()} detection.
+     *
+     * @throws URISyntaxException Should never happen.
+     */
+    @Test
+    public void testGetType() throws URISyntaxException {
+        final XLink link = new XLink();
+        int hashCode = link.hashCode();
+        assertFalse(hashCode == 0);
+        assertNull(link.getType());
+
+        link.setType(XLink.Type.AUTO);
+        assertEquals(XLink.Type.TITLE, link.getType());
+        assertEquals("XLink[type=\"title\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setRole(new URI("org:geotoolkit:role"));
+        assertEquals(XLink.Type.EXTENDED, link.getType());
+        assertEquals("XLink[type=\"extended\", role=\"org:geotoolkit:role\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setTitle(new SimpleInternationalString("Some title"));
+        assertEquals(XLink.Type.EXTENDED, link.getType());
+        assertEquals("XLink[type=\"extended\", role=\"org:geotoolkit:role\", title=\"Some title\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setLabel("SomeLabel");
+        assertEquals(XLink.Type.RESOURCE, link.getType());
+        assertEquals("XLink[type=\"resource\", role=\"org:geotoolkit:role\", title=\"Some title\", label=\"SomeLabel\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setHRef(new URI("org:geotoolkit:href"));
+        assertEquals(XLink.Type.LOCATOR, link.getType());
+        assertEquals("XLink[type=\"locator\", href=\"org:geotoolkit:href\", role=\"org:geotoolkit:role\", title=\"Some title\", label=\"SomeLabel\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setShow(XLink.Show.NEW);
+        assertNull("Can't be Type.SIMPLE if a label is defined.", link.getType());
+        assertEquals("XLink[href=\"org:geotoolkit:href\", role=\"org:geotoolkit:role\", title=\"Some title\", show=\"new\", label=\"SomeLabel\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setLabel(null);
+        assertEquals(XLink.Type.SIMPLE, link.getType());
+        assertEquals("XLink[type=\"simple\", href=\"org:geotoolkit:href\", role=\"org:geotoolkit:role\", title=\"Some title\", show=\"new\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+
+        link.setActuate(XLink.Actuate.ON_LOAD);
+        assertEquals(XLink.Type.SIMPLE, link.getType());
+        assertEquals("XLink[type=\"simple\", href=\"org:geotoolkit:href\", role=\"org:geotoolkit:role\", title=\"Some title\", show=\"new\", actuate=\"onLoad\"]", link.toString());
+        assertFalse("Hash code should have changed.", hashCode == (hashCode = link.hashCode()));
+        assertFalse("Hash code can not be zero.", hashCode == 0);
+        /*
+         * Now freezes the XLink and ensures that it is really immutable.
+         */
+        link.freeze();
+        assertEquals("hashCode", hashCode, link.hashCode());
+        try {
+            link.setType(null);
+            fail("The XLink should be unmodifiable.");
+        } catch (UnsupportedOperationException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("XLink"));
+        }
+    }
+
+    /**
+     * Tests write operation, which should not be allowed for some type of link.
+     *
+     * @throws URISyntaxException Should never happen.
+     */
+    @Test
+    public void testWrite() throws URISyntaxException {
+        final XLink link = new XLink();
+        link.setType(XLink.Type.SIMPLE);
+        link.setHRef(new URI("org:geotoolkit:href"));
+        assertEquals("XLink[type=\"simple\", href=\"org:geotoolkit:href\"]", link.toString());
+        try {
+            link.setLabel("SomeLabel");
+            fail("Should not be allowed to set the label.");
+        } catch (IllegalStateException e) {
+            // This is the expected exception. The message should contains the type name.
+            assertTrue(e.getMessage().contains("label"));
+            assertTrue(e.getMessage().contains("simple"));
+        }
+        assertEquals("XLink[type=\"simple\", href=\"org:geotoolkit:href\"]", link.toString());
+        try {
+            link.setType(XLink.Type.EXTENDED);
+            fail("Should not be allowed to set a type that does not include HREF.");
+        } catch (IllegalStateException e) {
+            // This is the expected exception. The message should contains the type name.
+            assertTrue(e.getMessage().contains("extended"));
+        }
+        assertEquals("XLink[type=\"simple\", href=\"org:geotoolkit:href\"]", link.toString());
+        /*
+         * The Locator type contains the HREF attribute, so the following operation should be
+         * allowed.
+         */
+        link.setType(XLink.Type.LOCATOR);
+        assertEquals("XLink[type=\"locator\", href=\"org:geotoolkit:href\"]", link.toString());
+        /*
+         * Now freezes the XLink and ensures that it is really immutable.
+         */
+        link.freeze();
+        try {
+            link.setHRef(null);
+            fail("The XLink should be unmodifiable.");
+        } catch (UnsupportedOperationException e) {
+            // This is the expected exception.
+            assertTrue(e.getMessage().contains("XLink"));
+        }
+    }
+
+    /**
+     * Tests equality.
+     *
+     * @throws URISyntaxException Should never happen.
+     */
+    @Test
+    public void testEquals() throws URISyntaxException {
+        final XLink link = new XLink();
+        link.setType(XLink.Type.AUTO);
+        link.setRole(new URI("org:geotoolkit:role"));
+        link.setTitle(new SimpleInternationalString("Some title"));
+        link.freeze();
+
+        final XLink other = new XLink();
+        assertFalse(link.equals(other));
+        assertFalse(link.hashCode() == other.hashCode());
+
+        other.setType(XLink.Type.AUTO);
+        assertFalse(link.equals(other));
+        assertFalse(link.hashCode() == other.hashCode());
+
+        other.setRole(new URI("org:geotoolkit:role"));
+        assertFalse(link.equals(other));
+        assertFalse(link.hashCode() == other.hashCode());
+
+        other.setTitle(new SimpleInternationalString("Some title"));
+        assertEquals(link, other);
+        assertEquals(link.hashCode(), other.hashCode());
+
+        other.freeze();
+        assertEquals(link, other);
+        assertEquals(link.hashCode(), other.hashCode());
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/xml/XLinkTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message