org.modeshape.jcr.DefinitionCache Maven / Gradle / Ivy
/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.jcr.nodetype.NodeType;
import org.modeshape.common.annotation.Immutable;
import org.modeshape.common.collection.LinkedListMultimap;
import org.modeshape.common.collection.Multimap;
import org.modeshape.jcr.value.Name;
/**
* A utility class that maintains a quick-lookup cache of all the child node definitions and property definitions for a series of
* node types. This class is used with {@link JcrNodeType} to represent these definitions for defined on the node type as well as
* those inherited from supertypes. However, it also can be used as a quick-lookup cache for a node's primary type and mixin
* types.
*/
@Immutable
final class DefinitionCache {
/**
* A local cache of all defined and inherited child node definitions, keyed by their name, that allow same-name-sibilings.
* This includes residual child node definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of
* this map is used frequently to find the most appropriate node definition for children, and so it is computed once at
* construction time (recall that all definitions, including the supertypes, are immutable).
*
* Note that a node type may have multiple child node definitions with the same name,
* "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
* specification). The order of the node definitions stored for each name is such that this node's definitions are stored
* first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
*
*/
private final Multimap childNodeDefinitionsThatAllowSns = LinkedListMultimap.create();
/**
* A local cache of all defined and inherited child node definitions, keyed by their name, that do not allow
* same-name-sibilings. This includes residual child node definitions, which are keyed by the
* {@link JcrNodeType#RESIDUAL_NAME}. The content of this map is used requently to find the most appropriate node definition
* for children, and so it is computed once at construction time (recall that all definitions, including the supertypes, are
* immutable).
*
* Note that a node type may have multiple child node definitions with the same name,
* "as long as they are distinguishable by the required primary types attribute." (Section 4.7.15 of the JSR-283 draft
* specification). The order of the node definitions stored for each name is such that this node's definitions are stored
* first, followed by those of the immediate supertypes, followed by those from those supertypes' supertypes, etc.
*
*/
private final Multimap childNodeDefinitionsThatAllowNoSns = LinkedListMultimap.create();
/**
* A local cache of all defined and inherited property definitions, keyed by their name, that allow multiple values. This
* includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
* is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
* it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
*
* Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
* otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
* or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
*
*/
private final Multimap multiValuedPropertyDefinitions = LinkedListMultimap.create();
/**
* A local cache of all defined and inherited property definitions, keyed by their name, that allow single values. This
* includes residual property definitions, which are keyed by the {@link JcrNodeType#RESIDUAL_NAME}. The content of this map
* is used frequently to find the most appropriate property definition for properties of nodes that use this node type, and so
* it is computed once at construction time (recall that all definitions, including supertypes, are immutable).
*
* Note that a node type may have multiple property node definitions with the same name, "as long as the definitions are
* otherwise distinguishable by either the required type attribute (the value returned by PropertyDefinition.getRequiredType)
* or the multiple attribute" (Section 4.7.15 of the JSR-283 draft specification).
*
*/
private final Multimap singleValuedPropertyDefinitions = LinkedListMultimap.create();
private final Multimap allChildNodeDefinitions = LinkedListMultimap.create();
private final Multimap allPropertyDefinitions = LinkedListMultimap.create();
DefinitionCache( JcrNodeType nodeType ) {
addDefinitionsForTypeAndAllSupertypes(nodeType);
}
DefinitionCache( JcrNodeType primaryType,
Iterable mixinTypes ) {
addDefinitionsForTypeAndAllSupertypes(primaryType);
// Assumption is that addMixin won't allow mixin types that conflict with primary types
if (mixinTypes != null) {
for (JcrNodeType mixinType : mixinTypes) {
addDefinitionsForTypeAndAllSupertypes(mixinType);
}
}
}
private final void addDefinitionsForTypeAndAllSupertypes( JcrNodeType nodeType ) {
// And the definitions from the type and all supertypes ...
for (NodeType superSuperType : nodeType.getTypeAndSupertypes()) {
addDefinitions((JcrNodeType)superSuperType);
}
}
private final void addDefinitions( JcrNodeType nodeType ) {
Set namesFromThisType = new HashSet();
for (JcrNodeDefinition definition : nodeType.childNodeDefinitions()) {
Name name = definition.getInternalName();
/*
* If the child node was already defined in the type hierarchy at some other level, ignore the definition
* - it was overridden by the previous definition. This relies on the fact that TypeA.getTypeAndSupertypes()
* always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is...
*
* ...UNLESS this is a residual definition, in which case side-by-side definitions must be allowed per 6.7.8
* of the 1.0.1 specification.
*/
if (allChildNodeDefinitions.containsKey(name) && !namesFromThisType.contains(name)
&& !JcrNodeType.RESIDUAL_NAME.equals(name)) {
continue;
}
if (definition.allowsSameNameSiblings()) {
childNodeDefinitionsThatAllowSns.put(name, definition);
} else {
childNodeDefinitionsThatAllowNoSns.put(name, definition);
}
allChildNodeDefinitions.put(name, definition);
namesFromThisType.add(name);
}
namesFromThisType.clear();
for (JcrPropertyDefinition definition : nodeType.propertyDefinitions()) {
Name name = definition.getInternalName();
/*
* If the property was already defined in the type hierarchy at some other level, ignore the definition
* - it was overridden by the previous definition. This relies on the fact that TypeA.getTypeAndSupertypes()
* always returns TypeX before TypeY if TypeX is closer to TypeA on the inheritance graph than TypeY is...
*
* ...UNLESS this is a residual definition, in which case side-by-side definitions must be allowed per 6.7.8
* of the 1.0.1 specification.
*/
if (allPropertyDefinitions.containsKey(name) && !namesFromThisType.contains(name)
&& !JcrNodeType.RESIDUAL_NAME.equals(name)) {
continue;
}
if (definition.isMultiple()) {
multiValuedPropertyDefinitions.put(name, definition);
} else {
singleValuedPropertyDefinitions.put(name, definition);
}
namesFromThisType.add(name);
allPropertyDefinitions.put(name, definition);
}
}
public Collection allSingleValuePropertyDefinitions( Name propertyName ) {
return this.singleValuedPropertyDefinitions.get(propertyName);
}
public Collection allMultiValuePropertyDefinitions( Name propertyName ) {
return this.multiValuedPropertyDefinitions.get(propertyName);
}
public Collection allPropertyDefinitions( Name propertyName ) {
return this.allPropertyDefinitions.get(propertyName);
}
public Collection allPropertyDefinitions() {
return this.allPropertyDefinitions.values();
}
public boolean anyPropertyDefinitions() {
return !this.allPropertyDefinitions.isEmpty();
}
public Collection allChildNodeDefinitionsWithNoSns( Name childName ) {
return this.childNodeDefinitionsThatAllowNoSns.get(childName);
}
public Collection allChildNodeDefinitionsWithSns( Name childName ) {
return this.childNodeDefinitionsThatAllowSns.get(childName);
}
public Collection allChildNodeDefinitions( Name childName ) {
return this.allChildNodeDefinitions.get(childName);
}
public Collection allChildNodeDefinitions( Name childName,
boolean requireSns ) {
if (requireSns) {
// Only return definitions that allow SNS since we require SNS support
return childNodeDefinitionsThatAllowSns.get(childName);
}
return allChildNodeDefinitions.get(childName);
}
public Collection allChildNodeDefinitions() {
return this.allChildNodeDefinitions.values();
}
public boolean anyChildNodeDefinitions() {
return !this.allChildNodeDefinitions.isEmpty();
}
}