/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.impl.core.nodetype.registration;
import org.exoplatform.services.jcr.core.nodetype.NodeDefinitionData;
import org.exoplatform.services.jcr.core.nodetype.NodeTypeData;
import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.LocationFactory;
import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeRepository;
import org.exoplatform.services.jcr.impl.core.value.ValueConstraintsValidator;
import org.exoplatform.services.jcr.impl.core.value.ValueFactoryImpl;
import org.exoplatform.services.jcr.impl.util.io.FileCleanerHolder;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
/**
* Created by The eXo Platform SAS.
*
* @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a>
* @version $Id$
*/
public class NodeTypeDataValidator
{
/**
* Class logger.
*/
private final static Log LOG = ExoLogger.getLogger("exo.jcr.component.core.NodeTypeDataValidator");
protected final NodeTypeRepository hierarchy;
protected final ValueFactoryImpl valueFactory;
protected class ValidatorValueFactory extends ValueFactoryImpl
{
ValidatorValueFactory(LocationFactory locationFactory, FileCleanerHolder cleanerHolder)
{
super(locationFactory, cleanerHolder);
}
/**
* {@inheritDoc}
*/
@Override
public Value createValue(String value, int type) throws ValueFormatException
{
if (type == PropertyType.BINARY)
{
LOG.warn("Not supported Value type: BINARY");
return null;
}
return super.createValue(value, type);
}
/**
* {@inheritDoc}
*/
@Override
public Value createValue(InputStream value)
{
LOG.warn("Not supported Value type: BINARY");
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Value loadValue(ValueData data, int type) throws RepositoryException
{
if (type == PropertyType.BINARY)
{
LOG.warn("Not supported Value type: BINARY");
return null;
}
return super.loadValue(data, type);
}
}
public NodeTypeDataValidator(LocationFactory locationFactory, NodeTypeRepository hierarchy,
FileCleanerHolder cleanerHolder)
{
this.hierarchy = hierarchy;
this.valueFactory = new ValidatorValueFactory(locationFactory, cleanerHolder);
}
public void validateNodeType(List<NodeTypeData> nodeTypeDataList) throws RepositoryException
{
for (NodeTypeData nodeTypeData : nodeTypeDataList)
{
validateNodeType(nodeTypeData);
}
checkCyclicDependencies(nodeTypeDataList);
}
private void checkCyclicDependencies(List<NodeTypeData> nodeTypeDataList) throws RepositoryException
{
Set<InternalQName> unresolvedDependecies = new HashSet<InternalQName>();
Set<InternalQName> resolvedDependecies = new HashSet<InternalQName>();
for (NodeTypeData nodeTypeData : nodeTypeDataList)
{
// / add itself
resolvedDependecies.add(nodeTypeData.getName());
// remove from unresolved
unresolvedDependecies.remove(nodeTypeData.getName());
// check suppers
for (int i = 0; i < nodeTypeData.getDeclaredSupertypeNames().length; i++)
{
InternalQName superName = nodeTypeData.getDeclaredSupertypeNames()[i];
if (hierarchy.getNodeType(superName) == null && !resolvedDependecies.contains(superName))
{
unresolvedDependecies.add(superName);
}
}
// check node definition
for (int i = 0; i < nodeTypeData.getDeclaredChildNodeDefinitions().length; i++)
{
NodeDefinitionData childnodeDefinitionData = nodeTypeData.getDeclaredChildNodeDefinitions()[i];
for (int j = 0; j < childnodeDefinitionData.getRequiredPrimaryTypes().length; j++)
{
InternalQName requiredPrimaryTypeName = childnodeDefinitionData.getRequiredPrimaryTypes()[j];
if (hierarchy.getNodeType(requiredPrimaryTypeName) == null
&& !resolvedDependecies.contains(requiredPrimaryTypeName))
{
unresolvedDependecies.add(requiredPrimaryTypeName);
}
}
if (childnodeDefinitionData.getDefaultPrimaryType() != null)
{
if (hierarchy.getNodeType(childnodeDefinitionData.getDefaultPrimaryType()) == null
&& !resolvedDependecies.contains(childnodeDefinitionData.getDefaultPrimaryType()))
{
unresolvedDependecies.add(childnodeDefinitionData.getDefaultPrimaryType());
}
}
}
}
if (unresolvedDependecies.size() > 0)
{
StringBuilder msg = new StringBuilder("Fail. Unresolved cyclic dependecy for :");
for (InternalQName internalQName : resolvedDependecies)
{
msg.append(" ").append(internalQName.getAsString());
}
msg.append(" Unresolved ");
for (InternalQName internalQName : unresolvedDependecies)
{
msg.append(" ").append(internalQName.getAsString());
}
throw new RepositoryException(msg.toString());
}
}
/**
* Check according the JSR-170
*/
private void validateNodeType(NodeTypeData nodeType) throws RepositoryException
{
if (nodeType == null)
{
throw new RepositoryException("NodeType object " + nodeType + " is null");
}
if (nodeType.getName() == null)
{
throw new RepositoryException("NodeType implementation class " + nodeType.getClass().getName()
+ " is not supported in this method");
}
for (InternalQName sname : nodeType.getDeclaredSupertypeNames())
{
if (!nodeType.getName().equals(Constants.NT_BASE) && nodeType.getName().equals(sname))
{
throw new RepositoryException("Invalid super type name" + sname.getAsString());
}
}
for (PropertyDefinitionData pdef : nodeType.getDeclaredPropertyDefinitions())
{
if (!pdef.getDeclaringNodeType().equals(nodeType.getName()))
{
throw new RepositoryException("Invalid declared node type in property definitions with name "
+ pdef.getName().getAsString() + " not registred");
}
// validate default values
try
{
validateValueDefaults(pdef.getRequiredType(), pdef.getDefaultValues());
}
catch (ValueFormatException e)
{
throw new ValueFormatException("Default value is incompatible with Property type "
+ PropertyType.nameFromValue(pdef.getRequiredType()) + " of " + pdef.getName().getAsString()
+ " in nodetype " + nodeType.getName().getAsString(), e);
}
try
{
validateValueConstraints(pdef.getRequiredType(), pdef.getValueConstraints());
}
catch (ValueFormatException e)
{
throw new ValueFormatException("Constraints is incompatible with Property type "
+ PropertyType.nameFromValue(pdef.getRequiredType()) + " of " + pdef.getName().getAsString()
+ " in nodetype " + nodeType.getName().getAsString(), e);
}
}
for (NodeDefinitionData cndef : nodeType.getDeclaredChildNodeDefinitions())
{
if (!cndef.getDeclaringNodeType().equals(nodeType.getName()))
{
throw new RepositoryException("Invalid declared node type in child node definitions with name "
+ cndef.getName().getAsString() + " not registred");
}
}
}
private void validateValueDefaults(int requiredType, String[] defValues) throws ValueFormatException
{
if (requiredType != PropertyType.STRING && requiredType != PropertyType.UNDEFINED && requiredType != PropertyType.BINARY)
{
for (String dv : defValues)
{
valueFactory.createValue(dv, requiredType);
}
}
}
private void validateValueConstraints(int requiredType, String[] constraints) throws ValueFormatException
{
ValueConstraintsValidator validator = new ValueConstraintsValidator(constraints);
validator.validateFor(requiredType);
}
}