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      /**
98       * Checks that the fields derived from requiredPrimaryTypeNames are initialized.
99       * <p>
100      * This was pulled out of the constructor to make type registration more flexible by deferring node type lookup for required
101      * primary types until after type registration is complete. This allows, for example, nodes to have themselves as required
102      * primary types of their children.
103      * </p>
104      */
105     private void ensureRequiredPrimaryTypesLoaded() {
106         if (requiredPrimaryTypesByName != null) return;
107         this.requiredPrimaryTypes = new JcrNodeType[requiredPrimaryTypeNames.length];
108         for (int i = 0; i != requiredPrimaryTypeNames.length; ++i) {
109             this.requiredPrimaryTypes[i] = nodeTypeManager.getNodeType(requiredPrimaryTypeNames[i]);
110         }
111         Map<Name, JcrNodeType> requiredPrimaryTypesByName = new HashMap<Name, JcrNodeType>();
112         for (JcrNodeType requiredPrimaryType : requiredPrimaryTypes) {
113             requiredPrimaryTypesByName.put(requiredPrimaryType.getInternalName(), requiredPrimaryType);
114         }
115         this.requiredPrimaryTypesByName = Collections.unmodifiableMap(requiredPrimaryTypesByName);
116 
117     }
118 
119     /**
120      * Get the durable identifier for this node definition.
121      * 
122      * @return the node definition ID; never null
123      */
124     public NodeDefinitionId getId() {
125         if (id == null) {
126             // This is idempotent, so no need to lock
127             id = new NodeDefinitionId(this.declaringNodeType.getInternalName(), this.name, this.requiredPrimaryTypeNames);
128         }
129         return id;
130     }
131 
132     /**
133      * {@inheritDoc}
134      * 
135      * @see javax.jcr.nodetype.NodeDefinition#allowsSameNameSiblings()
136      */
137     public boolean allowsSameNameSiblings() {
138         return allowsSameNameSiblings;
139     }
140 
141     /**
142      * @return the name of the default primary type for this definition; may be null
143      */
144     final Name defaultPrimaryTypeName() {
145         return defaultPrimaryTypeName;
146     }
147 
148     /**
149      * {@inheritDoc}
150      * 
151      * @see javax.jcr.nodetype.NodeDefinition#getDefaultPrimaryType()
152      */
153     public NodeType getDefaultPrimaryType() {
154         // It is valid for this field to be null.
155         if (defaultPrimaryTypeName == null) {
156             return null;
157         }
158 
159         return nodeTypeManager.getNodeType(defaultPrimaryTypeName);
160     }
161 
162     /**
163      * {@inheritDoc}
164      * 
165      * @see javax.jcr.nodetype.NodeDefinition#getRequiredPrimaryTypes()
166      */
167     public NodeType[] getRequiredPrimaryTypes() {
168         ensureRequiredPrimaryTypesLoaded();
169         // Make a copy so that the caller can't modify our content ...
170         NodeType[] result = new NodeType[requiredPrimaryTypes.length];
171         for (int i = 0; i != requiredPrimaryTypes.length; ++i) {
172             result[i] = requiredPrimaryTypes[i];
173         }
174         return result;
175     }
176 
177     /**
178      * Returns the required primary type names for this object as specified in the constructor. This method is useful for callers
179      * that wish to access this information while this node definition's parent node is being registered.
180      * 
181      * @return the required primary type names
182      */
183     Name[] requiredPrimaryTypeNames() {
184         return this.requiredPrimaryTypeNames;
185     }
186 
187     /**
188      * Get the set of names of the primary types.
189      * 
190      * @return the required primary type names
191      */
192     Set<Name> getRequiredPrimaryTypeNames() {
193         ensureRequiredPrimaryTypesLoaded();
194         return requiredPrimaryTypesByName.keySet();
195     }
196 
197     /**
198      * Determine if this node definition will allow a child with the supplied primary type. This method checks this definition's
199      * {@link #getRequiredPrimaryTypes()} against the supplied primary type and its supertypes. The supplied primary type for the
200      * child must be or extend all of the types defined by the {@link #getRequiredPrimaryTypes() required primary types}.
201      * 
202      * @param childPrimaryType the primary type of the child
203      * @return true if the primary type of the child (or one of its supertypes) is one of the types required by this definition,
204      *         or false otherwise
205      */
206     final boolean allowsChildWithType( JcrNodeType childPrimaryType ) {
207         if (childPrimaryType == null) {
208             // The definition must have a default primary type ...
209             if (defaultPrimaryTypeName != null) {
210                 return true;
211             }
212             return false;
213         }
214         ensureRequiredPrimaryTypesLoaded();
215         // The supplied primary type must be or extend all of the required primary types ...
216         for (Name requiredPrimaryTypeName : requiredPrimaryTypesByName.keySet()) {
217             if (!childPrimaryType.isNodeType(requiredPrimaryTypeName)) return false;
218         }
219         return true;
220     }
221 
222     /**
223      * Creates a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
224      * <code>declaringNodeType</code>. Provided to support immutable pattern for this class.
225      * 
226      * @param declaringNodeType the declaring node type for the new <code>JcrNodeDefinition</code>
227      * @return a new <code>JcrNodeDefinition</code> that is identical to the current object, but with the given
228      *         <code>declaringNodeType</code>.
229      */
230     JcrNodeDefinition with( JcrNodeType declaringNodeType ) {
231         return new JcrNodeDefinition(this.context, declaringNodeType.nodeTypeManager(), declaringNodeType, name,
232                                      getOnParentVersion(), isAutoCreated(), isMandatory(), isProtected(),
233                                      allowsSameNameSiblings(), defaultPrimaryTypeName, requiredPrimaryTypeNames);
234     }
235 
236     JcrNodeDefinition with( ExecutionContext context ) {
237         return new JcrNodeDefinition(context, this.nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
238                                      isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
239                                      defaultPrimaryTypeName, requiredPrimaryTypeNames);
240     }
241 
242     JcrNodeDefinition with( RepositoryNodeTypeManager nodeTypeManager ) {
243         return new JcrNodeDefinition(this.context, nodeTypeManager, this.declaringNodeType, name, getOnParentVersion(),
244                                      isAutoCreated(), isMandatory(), isProtected(), allowsSameNameSiblings(),
245                                      defaultPrimaryTypeName, requiredPrimaryTypeNames);
246     }
247 
248     @Override
249     public int hashCode() {
250         return getId().toString().hashCode();
251     }
252 
253     @Override
254     public boolean equals( Object obj ) {
255         if (this == obj) return true;
256         if (obj == null) return false;
257         if (getClass() != obj.getClass()) return false;
258         JcrNodeDefinition other = (JcrNodeDefinition)obj;
259         if (id == null) {
260             if (other.id != null) return false;
261         } else if (!id.equals(other.id)) return false;
262         return true;
263     }
264 
265     /**
266      * {@inheritDoc}
267      * 
268      * @see java.lang.Object#toString()
269      */
270     @Override
271     public String toString() {
272         return getId().toString();
273     }
274 }