org.apache.jackrabbit.spi.commons.nodetype.NodeTypeDefDiff Maven / Gradle / Ivy
/*
* 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.spi.commons.nodetype;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.PropertyType;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QItemDefinition;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QValueConstraint;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
/**
* A NodeTypeDefDiff
represents the result of the comparison of
* two node type definitions.
*
* The result of the comparison can be categorized as one of the following types:
*
* NONE
indicates that there is no modification at all.
*
* A TRIVIAL
modification has no impact on the consistency
* of existing content. The following modifications are considered
* TRIVIAL
:
*
* - changing node type
orderableChildNodes
flag
* - changing node type
primaryItemName
value
* - adding non-
mandatory
property/child node
* - changing property/child node
protected
flag
* - changing property/child node
onParentVersion
value
* - changing property/child node
mandatory
flag to false
* - changing property/child node
autoCreated
flag
* - changing specific property/child node
name
to *
* - changing child node
defaultPrimaryType
* - changing child node
sameNameSiblings
flag to true
* - weaken child node
requiredPrimaryTypes
(e.g. by removing)
* - weaken property
valueConstraints
(e.g. by removing a constraint
* or by making a specific constraint less restrictive)
* - changing property
defaultValues
* - changing specific property
requiredType
to undefined
* - changing property
multiple
flag to true
*
*
* A MAJOR
modification potentially affects the
* consistency of existing content.
*
* All modifications that are not TRIVIAL
are considered
* MAJOR
.
*
* @see #getType()
*/
public class NodeTypeDefDiff {
/**
* no modification
*/
public static final int NONE = 0;
/**
* trivial modification: does not affect consistency of existing content
*/
public static final int TRIVIAL = 1;
/**
* major modification: does affect consistency of existing content
*/
public static final int MAJOR = 2;
private final QNodeTypeDefinition oldDef;
private final QNodeTypeDefinition newDef;
private int type;
private final List propDefDiffs = new ArrayList();
private final List childNodeDefDiffs = new ArrayList();
/**
* Constructor
* @param oldDef old definition
* @param newDef new definition
*/
private NodeTypeDefDiff(QNodeTypeDefinition oldDef, QNodeTypeDefinition newDef) {
this.oldDef = oldDef;
this.newDef = newDef;
if (oldDef.equals(newDef)) {
// definitions are identical
type = NONE;
} else {
// definitions are not identical, determine type of modification
// assume TRIVIAL change by default
type = TRIVIAL;
// check supertypes
int tmpType = supertypesDiff();
if (tmpType > type) {
type = tmpType;
}
// check mixin flag (MAJOR modification)
tmpType = mixinFlagDiff();
if (tmpType > type) {
type = tmpType;
}
// check abstract flag (MAJOR modification)
tmpType = abstractFlagDiff();
if (tmpType > type) {
type = tmpType;
}
// no need to check orderableChildNodes flag (TRIVIAL modification)
// no need to check queryable flag (TRIVIAL modification)
// check property definitions
PropDefDiffBuilder propDefDiffBuilder = new PropDefDiffBuilder(oldDef.getPropertyDefs(), newDef.getPropertyDefs());
propDefDiffs.addAll(propDefDiffBuilder.getChildItemDefDiffs());
tmpType = propDefDiffBuilder.getMaxType();
if (tmpType > type) {
type = tmpType;
}
// check child node definitions
ChildNodeDefDiffBuilder childNodeDefDiffBuilder = new ChildNodeDefDiffBuilder(oldDef.getChildNodeDefs(), newDef.getChildNodeDefs());
childNodeDefDiffs.addAll(childNodeDefDiffBuilder.getChildItemDefDiffs());
tmpType = childNodeDefDiffBuilder.getMaxType();
if (tmpType > type) {
type = tmpType;
}
}
}
/**
* @param oldDef old definition
* @param newDef new definition
* @return the diff
*/
public static NodeTypeDefDiff create(QNodeTypeDefinition oldDef, QNodeTypeDefinition newDef) {
if (oldDef == null || newDef == null) {
throw new IllegalArgumentException("arguments can not be null");
}
if (!oldDef.getName().equals(newDef.getName())) {
throw new IllegalArgumentException("at least node type names must be matching");
}
return new NodeTypeDefDiff(oldDef, newDef);
}
/**
* @return true
if modified
*/
public boolean isModified() {
return type != NONE;
}
/**
* @return true
if trivial
*/
public boolean isTrivial() {
return type == TRIVIAL;
}
/**
* @return true
if major
*/
public boolean isMajor() {
return type == MAJOR;
}
/**
* Returns the type of modification as expressed by the following constants:
*
* NONE
: no modification at all
* TRIVIAL
: does not affect consistency of
* existing content
* MAJOR
: does affect consistency of existing
* content
*
*
* @return the type of modification
*/
public int getType() {
return type;
}
/**
* @return true
if mixin flag diff
*/
public int mixinFlagDiff() {
return oldDef.isMixin() != newDef.isMixin() ? MAJOR : NONE;
}
/**
* @return true
if abstract flag diff
*/
public int abstractFlagDiff() {
return oldDef.isAbstract() && !newDef.isAbstract() ? MAJOR : NONE;
}
/**
* @return true
if supertypes diff
*/
public int supertypesDiff() {
Set set1 = new HashSet(Arrays.asList(oldDef.getSupertypes()));
Set set2 = new HashSet(Arrays.asList(newDef.getSupertypes()));
return !set1.equals(set2) ? MAJOR : NONE;
}
@Override
public String toString() {
String result = getClass().getName() + "[\n\tnodeTypeName="
+ oldDef.getName();
result += ",\n\tmixinFlagDiff=" + modificationTypeToString(mixinFlagDiff());
result += ",\n\tsupertypesDiff=" + modificationTypeToString(supertypesDiff());
result += ",\n\tpropertyDifferences=[\n";
result += toString(propDefDiffs);
result += "\t]";
result += ",\n\tchildNodeDifferences=[\n";
result += toString(childNodeDefDiffs);
result += "\t]\n";
result += "]\n";
return result;
}
private String toString(List childItemDefDiffs) {
String result = "";
for (Iterator iter = childItemDefDiffs.iterator(); iter.hasNext();) {
ChildItemDefDiff propDefDiff = (ChildItemDefDiff) iter.next();
result += "\t\t" + propDefDiff;
if (iter.hasNext()) {
result += ",";
}
result += "\n";
}
return result;
}
private String modificationTypeToString(int modificationType) {
String typeString = "unknown";
switch (modificationType) {
case NONE:
typeString = "NONE";
break;
case TRIVIAL:
typeString = "TRIVIAL";
break;
case MAJOR:
typeString = "MAJOR";
break;
}
return typeString;
}
//--------------------------------------------------------< inner classes >
private abstract class ChildItemDefDiffBuilder> {
private final List childItemDefDiffs = new ArrayList();
private ChildItemDefDiffBuilder(T[] oldDefs, T[] newDefs) {
buildChildItemDefDiffs(collectChildNodeDefs(oldDefs), collectChildNodeDefs(newDefs));
}
private void buildChildItemDefDiffs(Map