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.util.Collection;
27  import java.util.HashSet;
28  import java.util.Set;
29  import javax.jcr.nodetype.NodeType;
30  import net.jcip.annotations.Immutable;
31  import org.modeshape.graph.property.Name;
32  import com.google.common.collect.LinkedListMultimap;
33  import com.google.common.collect.Multimap;
34  
35  /**
36   * A utility class that maintains a quick-lookup cache of all the child node definitions and property definitions for a series of
37   * node types. This class is used with {@link JcrNodeType} to represent these definitions for defined on the node type as well as
38   * those inherited from supertypes. However, it also can be used as a quick-lookup cache for a node's primary type and mixin
39   * types.
40   */
41  @Immutable
42  final class DefinitionCache {
43  
44      /**
45       * A local cache of all defined and inherited child node definitions, keyed by their name, that allow same-name-sibilings.
46       * This includes residual child node definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of
47       * this map is used frequently to find the most appropriate node definition for children, and so it is computed once at
48       * construction time (recall that all definitions, including the supertypes, are immutable).
49       * <p>
50       * Note that a node type may have multiple child node definitions with the same name,
51       * "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
52       * specification). The order of the node definitions stored for each name is such that this node's definitions are stored
53       * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
54       * </p>
55       */
56      private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowSns = LinkedListMultimap.create();
57      /**
58       * A local cache of all defined and inherited child node definitions, keyed by their name, that do <i>not</i> allow
59       * same-name-sibilings. This includes residual child node definitions, which are keyed by the
60       * {@link JcrNodeType#RESIDUAL_NAME}. The content of this map is used requently to find the most appropriate node definition
61       * for children, and so it is computed once at construction time (recall that all definitions, including the supertypes, are
62       * immutable).
63       * <p>
64       * Note that a node type may have multiple child node definitions with the same name,
65       * "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
66       * specification). The order of the node definitions stored for each name is such that this node's definitions are stored
67       * first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
68       * </p>
69       */
70      private final Multimap<Name, JcrNodeDefinition> childNodeDefinitionsThatAllowNoSns = LinkedListMultimap.create();
71  
72      /**
73       * A local cache of all defined and inherited property definitions, keyed by their name, that allow multiple values. This
74       * includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
75       * is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
76       * it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
77       * <p>
78       * Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
79       * otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
80       * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
81       * </p>
82       */
83      private final Multimap<Name, JcrPropertyDefinition> multiValuedPropertyDefinitions = LinkedListMultimap.create();
84      /**
85       * A local cache of all defined and inherited property definitions, keyed by their name, that allow single values. This
86       * includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
87       * is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
88       * it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
89       * <p>
90       * Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
91       * otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
92       * or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
93       * </p>
94       */
95      private final Multimap<Name, JcrPropertyDefinition> singleValuedPropertyDefinitions = LinkedListMultimap.create();
96  
97      private final Multimap<Name, JcrNodeDefinition> allChildNodeDefinitions = LinkedListMultimap.create();
98      private final Multimap<Name, JcrPropertyDefinition> allPropertyDefinitions = LinkedListMultimap.create();
99  
100     DefinitionCache( JcrNodeType nodeType ) {
101         addDefinitionsForTypeAndAllSupertypes(nodeType);
102     }
103 
104     DefinitionCache( JcrNodeType primaryType,
105                      Iterable<JcrNodeType> mixinTypes ) {
106         addDefinitionsForTypeAndAllSupertypes(primaryType);
107         // Assumption is that addMixin won't allow mixin types that conflict with primary types
108         if (mixinTypes != null) {
109             for (JcrNodeType mixinType : mixinTypes) {
110                 addDefinitionsForTypeAndAllSupertypes(mixinType);
111             }
112         }
113     }
114 
115     private final void addDefinitionsForTypeAndAllSupertypes( JcrNodeType nodeType ) {
116         // And the definitions from the type and all supertypes ...
117         for (NodeType superSuperType : nodeType.getTypeAndSupertypes()) {
118             addDefinitions((JcrNodeType)superSuperType);
119         }
120     }
121 
122     private final void addDefinitions( JcrNodeType nodeType ) {
123         Set<Name> namesFromThisType = new HashSet<Name>();
124 
125         for (JcrNodeDefinition definition : nodeType.childNodeDefinitions()) {
126             Name name = definition.getInternalName();
127 
128             /*
129              * If the child node was already defined in the type hierarchy at some other level, ignore the definition 
130              * - it was overridden by the previous definition.  This relies on the fact that TypeA.getTypeAndSupertypes()
131              * always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is.
132              */
133             if (allChildNodeDefinitions.containsKey(name) && !namesFromThisType.contains(name)) {
134                 continue;
135             }
136 
137             if (definition.allowsSameNameSiblings()) {
138                 childNodeDefinitionsThatAllowSns.put(name, definition);
139             } else {
140                 childNodeDefinitionsThatAllowNoSns.put(name, definition);
141             }
142             allChildNodeDefinitions.put(name, definition);
143             namesFromThisType.add(name);
144         }
145 
146         namesFromThisType.clear();
147         for (JcrPropertyDefinition definition : nodeType.propertyDefinitions()) {
148             Name name = definition.getInternalName();
149 
150             /*
151              * If the property was already defined in the type hierarchy at some other level, ignore the definition 
152              * - it was overridden by the previous definition.  This relies on the fact that TypeA.getTypeAndSupertypes()
153              * always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is.
154              */
155             if (allPropertyDefinitions.containsKey(name) && !namesFromThisType.contains(name)) {
156                 continue;
157             }
158             if (definition.isMultiple()) {
159                 multiValuedPropertyDefinitions.put(name, definition);
160             } else {
161                 singleValuedPropertyDefinitions.put(name, definition);
162             }
163             namesFromThisType.add(name);
164             allPropertyDefinitions.put(name, definition);
165         }
166     }
167 
168     public Collection<JcrPropertyDefinition> allSingleValuePropertyDefinitions( Name propertyName ) {
169         return this.singleValuedPropertyDefinitions.get(propertyName);
170     }
171 
172     public Collection<JcrPropertyDefinition> allMultiValuePropertyDefinitions( Name propertyName ) {
173         return this.multiValuedPropertyDefinitions.get(propertyName);
174     }
175 
176     public Collection<JcrPropertyDefinition> allPropertyDefinitions( Name propertyName ) {
177         return this.allPropertyDefinitions.get(propertyName);
178     }
179 
180     public Collection<JcrPropertyDefinition> allPropertyDefinitions() {
181         return this.allPropertyDefinitions.values();
182     }
183 
184     public Collection<JcrNodeDefinition> allChildNodeDefinitionsWithNoSns( Name childName ) {
185         return this.childNodeDefinitionsThatAllowNoSns.get(childName);
186     }
187 
188     public Collection<JcrNodeDefinition> allChildNodeDefinitionsWithSns( Name childName ) {
189         return this.childNodeDefinitionsThatAllowSns.get(childName);
190     }
191 
192     public Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName ) {
193         return this.allChildNodeDefinitions.get(childName);
194     }
195 
196     public Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
197                                                                   boolean requireSns ) {
198         if (requireSns) {
199             // Only return definitions that allow SNS since we require SNS support
200             return childNodeDefinitionsThatAllowSns.get(childName);
201         }
202         return allChildNodeDefinitions.get(childName);
203     }
204 
205     public Collection<JcrNodeDefinition> allChildNodeDefinitions() {
206         return this.allChildNodeDefinitions.values();
207     }
208 }