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    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed 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.util.Collections;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.Set;
30  import javax.jcr.nodetype.NodeDefinition;
31  import javax.jcr.nodetype.NodeType;
32  import net.jcip.annotations.Immutable;
33  import org.modeshape.graph.ExecutionContext;
34  import org.modeshape.graph.property.Name;
35  
36  /**
37   * ModeShape implementation of the {@link NodeDefinition} class.
38   */
39  @Immutable
40  class JcrNodeDefinition extends JcrItemDefinition implements NodeDefinition {
41  
42      /** @see NodeDefinition#allowsSameNameSiblings() */
43      private final boolean allowsSameNameSiblings;
44  
45      /**
46       * The name of the default primary type (if any). The name is used instead of the raw node type to allow circular references a
47       * la <code>nt:unstructured</code>.
48       */
49      private final Name defaultPrimaryTypeName;
50  
51      /** @see NodeDefinition#getRequiredPrimaryTypes() */
52      private Map<Name, JcrNodeType> requiredPrimaryTypesByName;
53  
54      private JcrNodeType[] requiredPrimaryTypes;
55  
56      private Name[] requiredPrimaryTypeNames;
57  
58      /** A durable identifier for this node definition. */
59      private NodeDefinitionId id;
60  
61      /** Link to the repository node type manager */
62      private final RepositoryNodeTypeManager nodeTypeManager;
63  
64      JcrNodeDefinition( ExecutionContext context,
65                         JcrNodeType declaringNodeType,
66                         Name name,
67                         int onParentVersion,
68                         boolean autoCreated,
69                         boolean mandatory,
70                         boolean protectedItem,
71                         boolean allowsSameNameSiblings,
72                         Name defaultPrimaryTypeName,
73                         Name[] requiredPrimaryTypeNames ) {
74          this(context, null, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem,
75               allowsSameNameSiblings, defaultPrimaryTypeName, requiredPrimaryTypeNames);
76      }
77  
78      JcrNodeDefinition( ExecutionContext context,
79                         RepositoryNodeTypeManager nodeTypeManager,
80                         JcrNodeType declaringNodeType,
81                         Name name,
82                         int onParentVersion,
83                         boolean autoCreated,
84                         boolean mandatory,
85                         boolean protectedItem,
86                         boolean allowsSameNameSiblings,
87                         Name defaultPrimaryTypeName,
88                         Name[] requiredPrimaryTypeNames ) {
89          super(context, declaringNodeType, name, onParentVersion, autoCreated, mandatory, protectedItem);
90          this.nodeTypeManager = nodeTypeManager;
91          this.allowsSameNameSiblings = allowsSameNameSiblings;
92          this.defaultPrimaryTypeName = defaultPrimaryTypeName;
93          this.requiredPrimaryTypes = new JcrNodeType[requiredPrimaryTypeNames.length];
94          this.requiredPrimaryTypeNames = requiredPrimaryTypeNames;
95      }
96  
97      private final String string( Name name ) {
98          if (name == null) return null;
99          if (this.context == null) return name.getString();
100 
101         return name.getString(context.getNamespaceRegistry());
102     }
103 
104     /**
105      * Checks that the fields derived from requiredPrimaryTypeNames are initialized.
106      * <p>
107      * This was pulled out of the constructor to make type registration more flexible by deferring node type lookup for required
108      * primary types until after type registration is complete. This allows, for example, nodes to have themselves as required
109      * primary types of their children.
110      * </p>
111      */
112     private void ensureRequiredPrimaryTypesLoaded() {
113         if (requiredPrimaryTypesByName != null) return;
114         this.requiredPrimaryTypes = new JcrNodeType[requiredPrimaryTypeNames.length];
115         for (int i = 0; i != requiredPrimaryTypeNames.length; ++i) {
116             this.requiredPrimaryTypes[i] = nodeTypeManager.getNodeType(requiredPrimaryTypeNames[i]);
117         }
118         Map<Name, JcrNodeType> requiredPrimaryTypesByName = new HashMap<Name, JcrNodeType>();
119         for (JcrNodeType requiredPrimaryType : requiredPrimaryTypes) {
120             requiredPrimaryTypesByName.put(requiredPrimaryType.getInternalName(), requiredPrimaryType);
121         }
122         this.requiredPrimaryTypesByName = Collections.unmodifiableMap(requiredPrimaryTypesByName);
123 
124     }
125 
126     /**
127      * Get the durable identifier for this node definition.
128      * 
129      * @return the node definition ID; never null
130      */
131     public NodeDefinitionId getId() {
132         if (id == null) {
133             // This is idempotent, so no need to lock
134             id = new NodeDefinitionId(this.declaringNodeType.getInternalName(), this.name, this.requiredPrimaryTypeNames);
135         }
136         return id;
137     }
138 
139     /**
140      * {@inheritDoc}
141      * 
142      * @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings()
143      */
144     public boolean allowsSameNameSiblings() {
145         return allowsSameNameSiblings;
146     }
147 
148     /**
149      * @return the name of the default primary type for this definition; may be null
150      */
151     final Name defaultPrimaryTypeName() {
152         return defaultPrimaryTypeName;
153     }
154 
155     /**
156      * {@inheritDoc}
157      * 
158      * @see javax.jcr.nodetype.NodeDefinition#getDefaultPrimaryType()
159      */
160     public NodeType getDefaultPrimaryType() {
161         // It is valid for this field to be null.
162         if (defaultPrimaryTypeName == null) {
163             return null;
164         }
165 
166         return nodeTypeManager.getNodeType(defaultPrimaryTypeName);
167     }
168 
169     /**
170      * {@inheritDoc}
171      * 
172      * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes()
173      */
174     public NodeType[] getRequiredPrimaryTypes() {
175         ensureRequiredPrimaryTypesLoaded();
176         if (requiredPrimaryTypes.length == 0) {
177             // Per the JavaDoc, this method should never return null or an empty array; if there are no constraints,
178             // then this method should include an array with 'nt:base' as the required primary type.
179             NodeType[] result = new NodeType[1];
180             result[0] = nodeTypeManager.getNodeType(JcrNtLexicon.BASE);
181             return result;
182         }
183         // Make a copy so that the caller can't modify our content ...
184         NodeType[] result = new NodeType[requiredPrimaryTypes.length];
185         for (int i = 0; i != requiredPrimaryTypes.length; ++i) {
186             result[i] = requiredPrimaryTypes[i];
187         }
188         return result;
189     }
190 
191     /**
192      * Returns the required primary type names for this object as specified in the constructor. This method is useful for callers
193      * that wish to access this information while this node definition's parent node is being registered.
194      * 
195      * @return the required primary type names
196      */
197     Name[] requiredPrimaryTypeNames() {
198         return this.requiredPrimaryTypeNames;
199     }
200 
201     /**
202      * Get the set of names of the primary types.
203      * 
204      * @return the required primary type names
205      */
206     Set<Name> requiredPrimaryTypeNameSet() {
207         ensureRequiredPrimaryTypesLoaded();
208         return requiredPrimaryTypesByName.keySet();
209     }
210 
211     /**
212      * @return the names of the required primary types for this child node definition; never null
213      */
214     public String[] getRequiredPrimaryTypeNames() {
215         if (requiredPrimaryTypeNames == null) return new String[0];
216 
217         String[] rptNames = new String[requiredPrimaryTypeNames.length];
218         for (int i = 0; i < requiredPrimaryTypeNames.length; i++) {
219             rptNames[i] = string(requiredPrimaryTypeNames[i]);
220         }
221         return rptNames;
222     }
223 
224     /**
225      * @return the name of the default primary type for this child node definition; may be null
226      */
227     public String getDefaultPrimaryTypeName() {
228         return string(this.defaultPrimaryTypeName);
229     }
230 
231     /**
232      * Determine if this node definition will allow a child with the supplied primary type. This method checks this definition's
233      * {@link #getRequiredPrimaryTypes()} against the supplied primary type and its supertypes. The supplied primary type for the
234      * child must be or extend all of the types defined by the {@link #getRequiredPrimaryTypes() required primary types}.
235      * 
236      * @param childPrimaryType the primary type of the child
237      * @return true if the primary type of the child (or one of its supertypes) is one of the types required by this definition,
238      *         or false otherwise
239      */
240     final boolean allowsChildWithType( JcrNodeType childPrimaryType ) {
241         if (childPrimaryType == null) {
242             // The definition must have a default primary type ...
243             if (defaultPrimaryTypeName != null) {
244                 return true;
245             }
246             return false;
247         }
248         ensureRequiredPrimaryTypesLoaded();
249         // The supplied primary type must be or extend all of the required primary types ...
250         for (Name requiredPrimaryTypeName : requiredPrimaryTypesByName.keySet()) {
251             if (!childPrimaryType.isNodeType(requiredPrimaryTypeName)) return false;
252         }
253         return true;
254     }
255 
256     /**
257      * Creates a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
258      * <code>declaringNodeType</code>. Provided to support immutable pattern for this class.
259      * 
260      * @param declaringNodeType the declaring node type for the new <code>JcrNodeDefinition</code>
261      * @return a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
262      *         <code>declaringNodeType</code>.
263      */
264     JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
265         return new JcrNodeDefinition(this.context, declaringNodeType.nodeTypeManager(), declaringNodeType, name,
266                                      getOnParentVersion(), isAutoCreated(), isMandatory(), isProtected(),
267                                      allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypeNames);
268     }
269 
270     JcrNodeDefinition with( ExecutionContext context ) {
271         return new JcrNodeDefinition(context, this.nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
272                                      isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
273                                      defaultPrimaryTypeName, requiredPrimaryTypeNames);
274     }
275 
276     JcrNodeDefinition with( RepositoryNodeTypeManager nodeTypeManager ) {
277         return new JcrNodeDefinition(this.context, nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
278                                      isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
279                                      defaultPrimaryTypeName, requiredPrimaryTypeNames);
280     }
281 
282     @Override
283     public int hashCode() {
284         return getId().toString().hashCode();
285     }
286 
287     @Override
288     public boolean equals( Object obj ) {
289         if (this == obj) return true;
290         if (obj == null) return false;
291         if (getClass() != obj.getClass()) return false;
292         JcrNodeDefinition other = (JcrNodeDefinition)obj;
293         if (id == null) {
294             if (other.id != null) return false;
295         } else if (!id.equals(other.id)) return false;
296         return true;
297     }
298 
299     /**
300      * {@inheritDoc}
301      * 
302      * @see java.lang.Object#toString()
303      */
304     @Override
305     public String toString() {
306         return getId().toString();
307     }
308 }