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      * Determine whether this node definition defines any named child.
127      * 
128      * @return true if this node definition allows children with any name, or false if this definition requires a particular child
129      *         name
130      */
131     public boolean allowsAnyChildName() {
132         return childDefinitionName.getLocalName().equals(ANY_NAME) && childDefinitionName.getNamespaceUri().length() == 0;
133     }
134 
135     /**
136      * Get the string form of this identifier. This form can be persisted, since it does not rely upon namespace prefixes.
137      * 
138      * @return the string form
139      */
140     public String getString() {
141         return this.stringRepresentation;
142     }
143 
144     /**
145      * Parse the supplied string for of an identifer, and return the object form for that identifier.
146      * 
147      * @param definition the {@link #getString() string form of the identifier}; may not be null
148      * @param factory the factory that should be used to create Name objects; may not be null
149      * @return the object form of the identifier; never null
150      * @throws ValueFormatException if the definition is not the valid format
151      */
152     public static NodeDefinitionId fromString( String definition,
153                                                NameFactory factory ) {
154         String[] parts = definition.split("/");
155         String nodeTypeNameString = parts[0];
156         String childDefinitionNameString = parts[1];
157         Name[] requiredPrimaryTypes = new Name[parts.length - 2];
158         for (int i = 2, j = 0; i != parts.length; ++i, ++j) {
159             requiredPrimaryTypes[j] = factory.create(parts[i]);
160         }
161         Name nodeTypeName = factory.create(nodeTypeNameString);
162         Name childDefinitionName = factory.create(childDefinitionNameString);
163         return new NodeDefinitionId(nodeTypeName, childDefinitionName, requiredPrimaryTypes);
164     }
165 
166     /**
167      * {@inheritDoc}
168      * 
169      * @see java.lang.Object#hashCode()
170      */
171     @Override
172     public int hashCode() {
173         return stringRepresentation.hashCode();
174     }
175 
176     /**
177      * {@inheritDoc}
178      * 
179      * @see java.lang.Object#equals(java.lang.Object)
180      */
181     @Override
182     public boolean equals( Object obj ) {
183         if (obj == this) return true;
184         if (obj instanceof NodeDefinitionId) {
185             NodeDefinitionId that = (NodeDefinitionId)obj;
186             return this.stringRepresentation.equals(that.stringRepresentation);
187         }
188         return false;
189     }
190 
191     /**
192      * {@inheritDoc}
193      * 
194      * @see java.lang.Object#toString()
195      */
196     @Override
197     public String toString() {
198         return this.stringRepresentation;
199     }
200 
201 }