View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * Unless otherwise indicated, all code in ModeShape is licensed
10   * to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jcr;
25  
26  import java.io.Serializable;
27  import java.util.HashMap;
28  import net.jcip.annotations.Immutable;
29  import org.modeshape.graph.property.Name;
30  import org.modeshape.graph.property.NameFactory;
31  import org.modeshape.graph.property.ValueFormatException;
32  
33  /**
34   * An immutable identifier for a node definition. Although instances can be serialized, the node definitions are often stored
35   * within the graph as {@link #getString() string values} on a property. These string values can later be
36   * {@link #fromString(String, NameFactory) parsed} to reconstruct the identifier. Note that this string representation does not
37   * use namespace prefixes, so they are long-lasting and durable.
38   * <p>
39   * What distinguishes one property definition from another is not well documented in the JSR-170 specification. The closest this
40   * version of the spec gets is Section 6.7.15, but that merely says that more than one property definition can have the same name.
41   * The proposed draft of the JSR-283 specification does clarify this more: Section 4.7.15 says :
42   * </p>
43   * <p>
44   * <quote>"Similarly, a node type may have two or more child node definitions with identical name attributes as long as they are
45   * distinguishable by the required primary types attribute (the value returned by
46   * NodeDefinition.getRequiredPrimaryTypes)."</quote>
47   * </p>
48   * <p>
49   * This class is {@link Serializable} and designed to be used as a key in a {@link HashMap}.
50   * </p>
51   */
52  @Immutable
53  final class NodeDefinitionId implements Serializable {
54  
55      /**
56       * Current version is {@value} .
57       */
58      private static final long serialVersionUID = 1L;
59  
60      /**
61       * The string-form of the name that can be used to represent a residual property definition.
62       */
63      public static final String ANY_NAME = JcrNodeType.RESIDUAL_ITEM_NAME;
64  
65      private final Name nodeTypeName;
66      private final Name childDefinitionName;
67      private final Name[] requiredPrimaryTypes;
68      /**
69       * A cached string representation, which is used for {@link #equals(Object)} and {@link #hashCode()} among other things.
70       */
71      private final String stringRepresentation;
72  
73      /**
74       * Create an identifier for a node definition.
75       * 
76       * @param nodeTypeName the name of the node type on which this child node definition is defined; may not be null
77       * @param childDefinitionName the name of the child node definition, which may be a {@link #ANY_NAME residual child
78       *        definition}; may not be null
79       * @param requiredPrimaryTypes the names of the required primary types for the child node definition
80       */
81      public NodeDefinitionId( Name nodeTypeName,
82                               Name childDefinitionName,
83                               Name[] requiredPrimaryTypes ) {
84          assert nodeTypeName != null;
85          assert childDefinitionName != null;
86          this.nodeTypeName = nodeTypeName;
87          this.childDefinitionName = childDefinitionName;
88          this.requiredPrimaryTypes = requiredPrimaryTypes;
89          StringBuilder sb = new StringBuilder(this.nodeTypeName.getString());
90          sb.append('/').append(this.childDefinitionName.getString());
91          for (Name requiredPrimaryType : requiredPrimaryTypes) {
92              sb.append('/');
93              sb.append(requiredPrimaryType.getString());
94          }
95          this.stringRepresentation = sb.toString();
96      }
97  
98      /**
99       * Get the name of the node type on which the child node definition is defined.
100      * 
101      * @return the node type's name; never null
102      */
103     public Name getNodeTypeName() {
104         return nodeTypeName;
105     }
106 
107     /**
108      * Get the name of the child definition.
109      * 
110      * @return the child definition's name; never null
111      */
112     public Name getChildDefinitionName() {
113         return childDefinitionName;
114     }
115 
116     /**
117      * @return requiredPrimaryTypes
118      */
119     public Name[] getRequiredPrimaryTypes() {
120         Name[] copy = new Name[requiredPrimaryTypes.length];
121         System.arraycopy(requiredPrimaryTypes, 0, copy, 0, requiredPrimaryTypes.length);
122         return copy;
123     }
124 
125     /**
126      * Return whether there is at least one {@link #getRequiredPrimaryTypes() required primary type}
127      * 
128      * @return true if there is at least one required primary type, or false otherewise
129      */
130     public boolean hasRequiredPrimaryTypes() {
131         return requiredPrimaryTypes.length != 0;
132     }
133 
134     /**
135      * Determine whether this node definition defines any named child.
136      * 
137      * @return true if this node definition allows children with any name, or false if this definition requires a particular child
138      *         name
139      */
140     public boolean allowsAnyChildName() {
141         return childDefinitionName.getLocalName().equals(ANY_NAME) && childDefinitionName.getNamespaceUri().length() == 0;
142     }
143 
144     /**
145      * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
146      * 
147      * @return the string form
148      */
149     public String getString() {
150         return this.stringRepresentation;
151     }
152 
153     /**
154      * Parse the supplied string for of an identifer, and return the object form for that identifier.
155      * 
156      * @param definition the {@link #getString() string form of the identifier}; may not be null
157      * @param factory the factory that should be used to create Name objects; may not be null
158      * @return the object form of the identifier; never null
159      * @throws ValueFormatException if the definition is not the valid format
160      */
161     public static NodeDefinitionId fromString( String definition,
162                                                NameFactory factory ) {
163         String[] parts = definition.split("/");
164         String nodeTypeNameString = parts[0];
165         String childDefinitionNameString = parts[1];
166         Name[] requiredPrimaryTypes = new Name[parts.length - 2];
167         for (int i = 2, j = 0; i != parts.length; ++i, ++j) {
168             requiredPrimaryTypes[j] = factory.create(parts[i]);
169         }
170         Name nodeTypeName = factory.create(nodeTypeNameString);
171         Name childDefinitionName = factory.create(childDefinitionNameString);
172         return new NodeDefinitionId(nodeTypeName, childDefinitionName, requiredPrimaryTypes);
173     }
174 
175     /**
176      * {@inheritDoc}
177      * 
178      * @see java.lang.Object#hashCode()
179      */
180     @Override
181     public int hashCode() {
182         return stringRepresentation.hashCode();
183     }
184 
185     /**
186      * {@inheritDoc}
187      * 
188      * @see java.lang.Object#equals(java.lang.Object)
189      */
190     @Override
191     public boolean equals( Object obj ) {
192         if (obj == this) return true;
193         if (obj instanceof NodeDefinitionId) {
194             NodeDefinitionId that = (NodeDefinitionId)obj;
195             return this.stringRepresentation.equals(that.stringRepresentation);
196         }
197         return false;
198     }
199 
200     /**
201      * {@inheritDoc}
202      * 
203      * @see java.lang.Object#toString()
204      */
205     @Override
206     public String toString() {
207         return this.stringRepresentation;
208     }
209 
210 }