/*
* 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.jcr2spi.nodetype;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.NodeId;
import org.apache.jackrabbit.spi.PathFactory;
import org.apache.jackrabbit.spi.PropertyId;
import org.apache.jackrabbit.spi.QItemDefinition;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.RepositoryService;
import org.apache.jackrabbit.spi.SessionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <code>ItemDefinitionManagerImpl</code>...
*/
public class ItemDefinitionProviderImpl implements ItemDefinitionProvider {
private static Logger log = LoggerFactory.getLogger(ItemDefinitionProviderImpl.class);
private final EffectiveNodeTypeProvider entProvider;
private final RepositoryService service;
private final SessionInfo sessionInfo;
private QNodeDefinition rootNodeDefinition;
public ItemDefinitionProviderImpl(EffectiveNodeTypeProvider entProvider,
RepositoryService service,
SessionInfo sessionInfo) {
this.entProvider = entProvider;
this.service = service;
this.sessionInfo = sessionInfo;
}
public QNodeDefinition getRootNodeDefinition() throws RepositoryException {
if (rootNodeDefinition == null) {
IdFactory idFactory = service.getIdFactory();
PathFactory pf = service.getPathFactory();
rootNodeDefinition = service.getNodeDefinition(
sessionInfo, idFactory.createNodeId((String) null, pf.getRootPath()));
}
return rootNodeDefinition;
}
public QNodeDefinition getQNodeDefinition(Name[] parentNodeTypeNames,
Name nodeName, Name ntName,
NodeId nodeId) throws RepositoryException {
if (parentNodeTypeNames == null) {
return getRootNodeDefinition();
}
QNodeDefinition definition;
try {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(parentNodeTypeNames);
EffectiveNodeType entTarget = getEffectiveNodeType(ntName);
definition = getQNodeDefinition(ent, entTarget, nodeName);
} catch (RepositoryException e) {
log.debug("Cannot determine effective node type of {}: {}", nodeId, e);
definition = getNodeDefinition(service, sessionInfo, nodeId);
}
return definition;
}
public QNodeDefinition getQNodeDefinition(Name[] parentNodeTypeNames, Name name, Name nodeTypeName)
throws NoSuchNodeTypeException, ConstraintViolationException {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(parentNodeTypeNames);
EffectiveNodeType entTarget = getEffectiveNodeType(nodeTypeName);
return getQNodeDefinition(ent, entTarget, name);
}
public QNodeDefinition getQNodeDefinition(EffectiveNodeType ent, Name name, Name nodeTypeName) throws NoSuchNodeTypeException, ConstraintViolationException {
EffectiveNodeType entTarget = getEffectiveNodeType(nodeTypeName);
return getQNodeDefinition(ent, entTarget, name);
}
public QPropertyDefinition getQPropertyDefinition(Name[] parentNodeTypeNames,
Name propertyName,
int propertType,
boolean isMultiValued,
PropertyId propertyId) throws RepositoryException {
QPropertyDefinition definition;
try {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(parentNodeTypeNames);
definition = getQPropertyDefinition(ent, propertyName, propertType, isMultiValued, true);
} catch (RepositoryException e) {
log.debug("Cannot determine property defintion of {}: {}", propertyId, e);
definition = getPropertyDefinition(service, sessionInfo, propertyId);
}
return definition;
}
public QPropertyDefinition getQPropertyDefinition(Name ntName, Name propName,
int type, boolean multiValued)
throws ConstraintViolationException, NoSuchNodeTypeException {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(ntName);
return getQPropertyDefinition(ent, propName, type, multiValued, false);
}
public QPropertyDefinition getQPropertyDefinition(Name[] parentNodeTypeNames,
Name name, int type,
boolean multiValued)
throws ConstraintViolationException, NoSuchNodeTypeException {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(parentNodeTypeNames);
return getQPropertyDefinition(ent, name, type, multiValued, false);
}
public QPropertyDefinition getQPropertyDefinition(Name[] parentNodeTypeNames,
Name name, int type)
throws ConstraintViolationException, NoSuchNodeTypeException {
EffectiveNodeType ent = entProvider.getEffectiveNodeType(parentNodeTypeNames);
return getQPropertyDefinition(ent, name, type);
}
//--------------------------------------------------------------------------
private EffectiveNodeType getEffectiveNodeType(Name ntName) throws NoSuchNodeTypeException {
if (ntName != null) {
return entProvider.getEffectiveNodeType(ntName);
} else {
return null;
}
}
/**
*
* @param ent
* @param entTarget
* @param name
* @return
* @throws ConstraintViolationException
*/
static QNodeDefinition getQNodeDefinition(EffectiveNodeType ent,
EffectiveNodeType entTarget,
Name name)
throws ConstraintViolationException {
// try named node definitions first
QNodeDefinition[] defs = ent.getNamedQNodeDefinitions(name);
if (defs != null) {
for (int i = 0; i < defs.length; i++) {
QNodeDefinition nd = defs[i];
// node definition with that name exists
if (entTarget != null && nd.getRequiredPrimaryTypes() != null) {
// check 'required primary types' constraint
if (entTarget.includesNodeTypes(nd.getRequiredPrimaryTypes())) {
// found named node definition
return nd;
}
} else {
if (nd.getDefaultPrimaryType() != null) {
// found node definition with default node type
return nd;
}
}
}
}
// no item with that name defined;
// try residual node definitions
QNodeDefinition[] nda = ent.getUnnamedQNodeDefinitions();
for (int i = 0; i < nda.length; i++) {
QNodeDefinition nd = nda[i];
if (entTarget != null && nd.getRequiredPrimaryTypes() != null) {
// check 'required primary types' constraint
if (entTarget.includesNodeTypes(nd.getRequiredPrimaryTypes())) {
// found residual node definition
return nd;
}
} else {
// since no node type has been specified for the new node,
// it must be determined from the default node type;
if (nd.getDefaultPrimaryType() != null) {
// found residual node definition with default node type
return nd;
}
}
}
// no applicable definition found
throw new ConstraintViolationException("no matching child node definition found for " + name);
}
/**
*
* @param ent
* @param name
* @param type
* @param multiValued
* @return
* @throws ConstraintViolationException
*/
private static QPropertyDefinition getQPropertyDefinition(EffectiveNodeType ent,
Name name, int type,
boolean multiValued, boolean throwWhenAmbiguous)
throws ConstraintViolationException {
// try named property definitions first
QPropertyDefinition[] defs = ent.getNamedQPropertyDefinitions(name);
QPropertyDefinition match = getMatchingPropDef(defs, type, multiValued, throwWhenAmbiguous);
if (match != null) {
return match;
}
// no item with that name defined;
// try residual property definitions
defs = ent.getUnnamedQPropertyDefinitions();
match = getMatchingPropDef(defs, type, multiValued, throwWhenAmbiguous);
if (match != null) {
return match;
}
// no applicable definition found
throw new ConstraintViolationException("no matching property definition found for " + name);
}
/**
*
* @param ent
* @param name
* @param type
* @return
* @throws ConstraintViolationException
*/
private static QPropertyDefinition getQPropertyDefinition(EffectiveNodeType ent,
Name name, int type)
throws ConstraintViolationException {
// try named property definitions first
QPropertyDefinition[] defs = ent.getNamedQPropertyDefinitions(name);
QPropertyDefinition match = getMatchingPropDef(defs, type);
if (match != null) {
return match;
}
// no item with that name defined;
// try residual property definitions
defs = ent.getUnnamedQPropertyDefinitions();
match = getMatchingPropDef(defs, type);
if (match != null) {
return match;
}
// no applicable definition found
throw new ConstraintViolationException("no matching property definition found for " + name);
}
private static QPropertyDefinition getMatchingPropDef(QPropertyDefinition[] defs, int type) {
QPropertyDefinition match = null;
for (int i = 0; i < defs.length; i++) {
QItemDefinition qDef = defs[i];
if (!qDef.definesNode()) {
QPropertyDefinition pd = (QPropertyDefinition) qDef;
int reqType = pd.getRequiredType();
// match type
if (reqType == PropertyType.UNDEFINED
|| type == PropertyType.UNDEFINED
|| reqType == type) {
if (match == null) {
match = pd;
} else {
// check if this definition is a better match than
// the one we've already got
if (match.getRequiredType() != pd.getRequiredType()) {
if (match.getRequiredType() == PropertyType.UNDEFINED) {
// found better match
match = pd;
}
} else {
if (match.isMultiple() && !pd.isMultiple()) {
// found better match
match = pd;
}
}
}
if (match.getRequiredType() != PropertyType.UNDEFINED
&& !match.isMultiple()) {
// found best possible match, get outta here
return match;
}
}
}
}
return match;
}
private static QPropertyDefinition getMatchingPropDef(QPropertyDefinition[] defs, int type,
boolean multiValued, boolean throwWhenAmbiguous)
throws ConstraintViolationException {
QPropertyDefinition match = null;
for (int i = 0; i < defs.length; i++) {
QItemDefinition qDef = defs[i];
if (!qDef.definesNode()) {
QPropertyDefinition pd = (QPropertyDefinition) qDef;
int reqType = pd.getRequiredType();
// match type
if (reqType == PropertyType.UNDEFINED
|| type == PropertyType.UNDEFINED
|| reqType == type) {
// match multiValued flag
if (multiValued == pd.isMultiple()) {
// found match
if (pd.getRequiredType() != PropertyType.UNDEFINED) {
if (match != null && throwWhenAmbiguous) {
throw new ConstraintViolationException("ambiguous property definitions found: " + match + " vs " + pd);
}
if (match != null && match.getRequiredType() == PropertyType.STRING) {
// If we already found a match, and that was of PropertyType.STRING,
// then do not overwrite it. The whole reason there are multiple
// potential matches is that the client did not specify the type,
// thus obviously specified a String.
}
else {
// found best possible match
match = pd;
}
} else {
if (match == null) {
match = pd;
}
}
}
}
}
}
return match;
}
private static QNodeDefinition getNodeDefinition(RepositoryService service, SessionInfo sessionInfo,
NodeId nodeId) throws RepositoryException {
try {
return service.getNodeDefinition(sessionInfo, nodeId);
}
catch (RepositoryException e) {
log.error("Cannot determine node definition of {}: {}", nodeId, e);
throw e;
}
}
private static QPropertyDefinition getPropertyDefinition(RepositoryService service,
SessionInfo sessionInfo, PropertyId propertyId) throws RepositoryException {
try {
return service.getPropertyDefinition(sessionInfo, propertyId);
}
catch (RepositoryException e) {
log.error("Cannot determine property definition of {}: {}", propertyId, e);
throw e;
}
}
}