blob: 88149cae93578a41b91e5cabb10508393aec23b1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.oak.plugins.nodetype;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.contains;
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
import static org.apache.jackrabbit.oak.api.Type.STRING;
import static org.apache.jackrabbit.oak.commons.PathUtils.dropIndexFromName;
import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.NODE_TYPES_PATH;
import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.REP_SUPERTYPES;
import java.util.Iterator;
import java.util.List;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFactory;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeDefinitionTemplate;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeDefinition;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.NodeTypeTemplate;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.nodetype.PropertyDefinitionTemplate;
import com.google.common.collect.Lists;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.iterator.NodeTypeIteratorAdapter;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.namepath.NameMapper;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.namepath.impl.NamePathMapperImpl;
import org.apache.jackrabbit.oak.spi.nodetype.DefinitionProvider;
import org.apache.jackrabbit.oak.spi.nodetype.EffectiveNodeType;
import org.apache.jackrabbit.oak.spi.nodetype.EffectiveNodeTypeProvider;
import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Base implementation of a {@link NodeTypeManager} with support for reading
* node types from the {@link Tree} returned by {@link #getTypes()}. Methods
* related to node type modifications throw
* {@link UnsupportedRepositoryOperationException}.
*/
public abstract class ReadOnlyNodeTypeManager implements NodeTypeManager, EffectiveNodeTypeProvider, DefinitionProvider {
/**
* Returns the internal name for the specified JCR name.
*
* @param jcrName JCR node type name.
* @return the internal representation of the given JCR name.
* @throws javax.jcr.RepositoryException If there is no valid internal representation
* of the specified JCR name.
*/
@NotNull
protected final String getOakName(String jcrName) throws RepositoryException {
return getNamePathMapper().getOakName(jcrName);
}
/**
* @return {@link org.apache.jackrabbit.oak.api.Tree} instance where the node types
* are stored or {@code null} if none.
*/
@Nullable
protected abstract Tree getTypes();
/**
* The value factory to be used by {@link org.apache.jackrabbit.oak.plugins.nodetype.PropertyDefinitionImpl#getDefaultValues()}.
* If {@code null} the former returns {@code null}.
* @return {@code ValueFactory} instance or {@code null}.
*/
@Nullable
protected ValueFactory getValueFactory() {
return null;
}
/**
* Returns a {@link NameMapper} to be used by this node type manager. This
* implementation returns the {@link NamePathMapperImpl#DEFAULT} instance. A
* subclass may override this method and provide a different
* implementation.
*
* @return {@link NameMapper} instance.
*/
@NotNull
protected NamePathMapper getNamePathMapper() {
return NamePathMapper.DEFAULT;
}
//--------------------------------------------------------------------------
/**
* Return a new instance of {@code ReadOnlyNodeTypeManager} that reads node
* type information from the tree at {@link NodeTypeConstants#NODE_TYPES_PATH}.
*
* @param root The root to read node types from.
* @param namePathMapper The {@code NamePathMapper} to use.
* @return a new instance of {@code ReadOnlyNodeTypeManager}.
*/
@NotNull
public static ReadOnlyNodeTypeManager getInstance(final Root root,
final NamePathMapper namePathMapper) {
return new ReadOnlyNodeTypeManager() {
@Override
protected Tree getTypes() {
return root.getTree(NODE_TYPES_PATH);
}
@NotNull
@Override
protected NamePathMapper getNamePathMapper() {
return namePathMapper;
}
};
}
//----------------------------------------------------< NodeTypeManager >---
@Override
public boolean hasNodeType(String name) throws RepositoryException {
Tree types = getTypes();
return types != null && types.hasChild(getOakName(name));
}
@Override
public NodeType getNodeType(String name) throws RepositoryException {
return internalGetNodeType(getOakName(name));
}
@Override
public NodeTypeIterator getAllNodeTypes() throws RepositoryException {
List<NodeType> list = Lists.newArrayList();
Tree types = getTypes();
if (types != null) {
NamePathMapper mapper = getNamePathMapper();
for (Tree type : types.getChildren()) {
list.add(new NodeTypeImpl(type, mapper));
}
}
return new NodeTypeIteratorAdapter(list);
}
@Override
public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException {
List<NodeType> list = Lists.newArrayList();
NodeTypeIterator iterator = getAllNodeTypes();
while (iterator.hasNext()) {
NodeType type = iterator.nextNodeType();
if (!type.isMixin()) {
list.add(type);
}
}
return new NodeTypeIteratorAdapter(list);
}
@Override
public NodeTypeIterator getMixinNodeTypes() throws RepositoryException {
List<NodeType> list = Lists.newArrayList();
NodeTypeIterator iterator = getAllNodeTypes();
while (iterator.hasNext()) {
NodeType type = iterator.nextNodeType();
if (type.isMixin()) {
list.add(type);
}
}
return new NodeTypeIteratorAdapter(list);
}
@Override
public NodeTypeTemplate createNodeTypeTemplate()
throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
@Override
public NodeTypeTemplate createNodeTypeTemplate(NodeTypeDefinition ntd)
throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
@Override
public NodeDefinitionTemplate createNodeDefinitionTemplate()
throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
@Override
public PropertyDefinitionTemplate createPropertyDefinitionTemplate()
throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
/**
* This implementation always throws a {@link UnsupportedRepositoryOperationException}.
*/
@Override
public NodeType registerNodeType(NodeTypeDefinition ntd, boolean allowUpdate) throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
/**
* This implementation always throws a {@link UnsupportedRepositoryOperationException}.
*/
@Override
public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
/**
* This implementation always throws a {@link UnsupportedRepositoryOperationException}.
*/
@Override
public void unregisterNodeType(String name) throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
/**
* This implementation always throws a {@link UnsupportedRepositoryOperationException}.
*/
@Override
public void unregisterNodeTypes(String[] names) throws RepositoryException {
throw new UnsupportedRepositoryOperationException();
}
//------------------------------------------< EffectiveNodeTypeProvider >---
@Override
public boolean isNodeType(Tree tree, String oakNtName) {
// shortcuts for common cases
if (JcrConstants.NT_BASE.equals(oakNtName)) {
return true;
} else if (JcrConstants.MIX_REFERENCEABLE.equals(oakNtName)
&& !tree.hasProperty(JcrConstants.JCR_UUID)) {
return false;
} else if (JcrConstants.MIX_VERSIONABLE.equals(oakNtName)
&& !tree.hasProperty(JcrConstants.JCR_ISCHECKEDOUT)) {
return false;
}
Tree types = getTypes();
PropertyState primary = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE);
if (primary != null && primary.getType() == Type.NAME) {
String name = primary.getValue(Type.NAME);
if (isa(types, name, oakNtName)) {
return true;
}
}
PropertyState mixins = tree.getProperty(JcrConstants.JCR_MIXINTYPES);
if (mixins != null && mixins.getType() == Type.NAMES) {
for (String name : mixins.getValue(Type.NAMES)) {
if (isa(types, name, oakNtName)) {
return true;
}
}
}
return false;
}
@Override
public boolean isNodeType(@Nullable String primaryTypeName, @NotNull Iterator<String> mixinTypes,
@NotNull String nodeTypeName) throws NoSuchNodeTypeException, RepositoryException {
// shortcut
if (JcrConstants.NT_BASE.equals(nodeTypeName)) {
return true;
}
Tree types = getTypes();
if (primaryTypeName != null && isa(types, primaryTypeName, nodeTypeName)) {
return true;
}
while (mixinTypes.hasNext()) {
if (isa(types, mixinTypes.next(), nodeTypeName)) {
return true;
}
}
return false;
}
private static boolean isa(Tree types, String typeName, String superName) {
if (typeName.equals(superName)) {
return true;
}
Tree type = types.getChild(typeName);
if (!type.exists()) {
return false;
}
PropertyState supertypes = type.getProperty(REP_SUPERTYPES);
return supertypes != null
&& contains(supertypes.getValue(Type.NAMES), superName);
}
@Override
public boolean isNodeType(String typeName, String superName) {
return isa(getTypes(), typeName, superName);
}
/**
* Returns all the node types of the given node, in a breadth-first
* traversal order of the type hierarchy.
*
* @param node node instance
* @return all types of the given node
* @throws RepositoryException if the type information can not be accessed
*/
@Override
public EffectiveNodeType getEffectiveNodeType(Node node)
throws RepositoryException {
NodeTypeImpl primary = (NodeTypeImpl) node.getPrimaryNodeType(); // FIXME
NodeType[] mixins = node.getMixinNodeTypes();
NodeTypeImpl[] mixinImpls = new NodeTypeImpl[mixins.length];
for (int i = 0; i < mixins.length; i++) {
mixinImpls[i] = (NodeTypeImpl) mixins[i]; // FIXME
}
return new EffectiveNodeTypeImpl(primary, mixinImpls, this);
}
@Override
public EffectiveNodeType getEffectiveNodeType(Tree tree) throws RepositoryException {
NodeTypeImpl primaryType;
PropertyState jcrPrimaryType = tree.getProperty(JCR_PRIMARYTYPE);
if (jcrPrimaryType != null) {
String ntName = jcrPrimaryType.getValue(STRING);
primaryType = internalGetNodeType(ntName);
} else {
throw new RepositoryException("Node at "+tree.getPath()+" has no primary type.");
}
PropertyState jcrMixinType = tree.getProperty(JCR_MIXINTYPES);
if (jcrMixinType == null) {
return new EffectiveNodeTypeImpl(primaryType, this);
} else {
NodeTypeImpl[] mixinTypes = new NodeTypeImpl[jcrMixinType.count()];
for (int i = 0; i < mixinTypes.length; i++) {
mixinTypes[i] = internalGetNodeType(jcrMixinType.getValue(Type.NAME, i));
}
return new EffectiveNodeTypeImpl(primaryType, mixinTypes, this);
}
}
//-------------------------------------------------< DefinitionProvider >---
@NotNull
@Override
public NodeDefinition getRootDefinition() throws RepositoryException {
return new RootNodeDefinition(this);
}
@NotNull
@Override
public NodeDefinition getDefinition(@NotNull Tree parent, @NotNull String nodeName)
throws RepositoryException {
checkNotNull(parent);
checkNotNull(nodeName);
EffectiveNodeType effective = getEffectiveNodeType(parent);
return effective.getNodeDefinition(nodeName, null);
}
@NotNull
@Override
public NodeDefinition getDefinition(
@NotNull Tree parent, @NotNull Tree targetNode)
throws RepositoryException {
checkNotNull(parent);
checkNotNull(targetNode);
String name = dropIndexFromName(targetNode.getName());
EffectiveNodeType eff = getEffectiveNodeType(parent);
return eff.getNodeDefinition(name, getEffectiveNodeType(targetNode));
}
@NotNull
@Override
public PropertyDefinition getDefinition(
Tree parent, PropertyState property, boolean exactTypeMatch)
throws RepositoryException {
Type<?> type = property.getType();
EffectiveNodeType effective = getEffectiveNodeType(parent);
return effective.getPropertyDefinition(
property.getName(), type.isArray(), type.tag(), exactTypeMatch);
}
//-----------------------------------------------------------< internal >---
NodeTypeImpl internalGetNodeType(String oakName) throws NoSuchNodeTypeException {
Tree types = getTypes();
if (types != null) {
Tree type = types.getChild(oakName);
if (type.exists()) {
return new NodeTypeImpl(type, getNamePathMapper());
}
}
throw new NoSuchNodeTypeException(getNamePathMapper().getJcrName(oakName));
}
}