ALF-7260: schema comparator

* replace path/push/pop mechanism as it doesn't work well at reporting where differences/validation errors occur.
* add getParent() to DbObject - so that a path-style identifier can be deduced for a DbObject when needed
* add DbProperty to specify a specific DbObject's property and value -- acts as a schema location pointer
* refactored Result code (need difference result and validation error result)





git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31527 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Matt Ward 2011-10-27 18:07:06 +00:00
parent 385003c6c9
commit 723fe98cf2
37 changed files with 1022 additions and 613 deletions

View File

@ -37,11 +37,11 @@ public interface ComparisonUtils
*/
DbObject findSameObjectAs(Collection<? extends DbObject> objects, final DbObject objToFind);
void compareSimpleCollections(Collection<? extends Object> leftCollection,
Collection<? extends Object> rightCollection, DiffContext ctx, Strength strength);
void compareSimpleCollections(DbProperty leftProperty, DbProperty rightProperty,
DiffContext ctx, Strength strength);
/**
* Compare collections, reporting differences using the default {@link Result.Strength}
* Compare collections, reporting differences using the default {@link Difference.Strength}
*
* @see #compareCollections(Collection, Collection, Differences, Strength)
*/
@ -50,7 +50,7 @@ public interface ComparisonUtils
/**
* Compare collections of {@link DbObject}s using their {@link DbObject#diff(DbObject, Differences)} method.
* Differences are reported using the specified {@link Result.Strength}.
* Differences are reported using the specified {@link Difference.Strength}.
*
* @param leftCollection
* @param rightCollection
@ -66,7 +66,7 @@ public interface ComparisonUtils
*
* @see #compareSimple(Object, Object, Differences, Strength)
*/
void compareSimple(Object left, Object right, DiffContext ctx);
void compareSimple(DbProperty left, DbProperty right, DiffContext ctx);
/**
* Compare two 'simple' (i.e. non-{@link DbObject} objects) using their {@link Object#equals(Object)} method
@ -77,6 +77,6 @@ public interface ComparisonUtils
* @param differences
* @param strength
*/
void compareSimple(Object left, Object right, DiffContext ctx, Strength strength);
void compareSimple(DbProperty left, DbProperty right, DiffContext ctx, Strength strength);
}

View File

@ -18,27 +18,14 @@
*/
package org.alfresco.util.schemacomp;
import org.alfresco.util.schemacomp.model.Column;
import org.alfresco.util.schemacomp.model.DbObject;
import org.alfresco.util.schemacomp.model.ForeignKey;
import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.PrimaryKey;
import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Sequence;
import org.alfresco.util.schemacomp.model.Table;
/**
* Defines a visitor that can operate on a DbObject.
*
* @author Matt Ward
*/
public interface DbObjectVisitor
{
void visit(DbObject dbObject);
// void visit(Column column);
// void visit(ForeignKey fk);
// void visit(Index index);
// void visit(PrimaryKey pk);
// void visit(Schema schema);
// void visit(Sequence sequence);
// void visit(Table table);
}

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.schemacomp;
import org.alfresco.util.schemacomp.model.DbObject;
import org.apache.commons.beanutils.PropertyUtils;
/**
* A pointer to a specific DbObject property and its value (at time of creating the DbProperty object).
*
* @author Matt Ward
*/
public class DbProperty
{
private final DbObject dbObject;
private final String propertyName;
private final Object propertyValue;
/**
* Full constructor allowing control over whether the property name should be indexed (e.g. colours[3]),
* whether the current value of the property should be retrieved automatically or whether to use the
* supplied value (useful when performing comparisons - construct one with a particular/expected value and
* construct another with the current value by reflection).
* <p>
* The public constructors provide a more usable API with select sets of arguments.
*
* @param dbObject
* @param propertyName
* @param propertyValue
*/
protected DbProperty(DbObject dbObject, String propertyName, int index, boolean useSuppliedValue, Object propertyValue)
{
if (dbObject == null)
{
throw new IllegalArgumentException("dbObject cannot be null.");
}
this.dbObject = dbObject;
if (propertyName == null)
{
if (index > -1 || useSuppliedValue)
{
throw new IllegalArgumentException("propertyName cannot be null.");
}
}
if (index > -1)
{
this.propertyName = propertyName+"["+index+"]";
}
else
{
this.propertyName = propertyName;
}
// Unfortunetely, this boolean is required, since we may want to set the property value to null.
if (useSuppliedValue)
{
this.propertyValue = propertyValue;
}
else if (propertyName != null)
{
try
{
this.propertyValue = PropertyUtils.getProperty(dbObject, this.propertyName);
}
catch (Throwable error)
{
throw new IllegalArgumentException("Cannot get value for property named \"" + propertyName + "\"", error);
}
}
else
{
// No property is being referred to by this object.
this.propertyValue = null;
}
}
/**
* Construct a pointer to a database object only (no property within).
*
* @param dbObject
*/
public DbProperty(DbObject dbObject)
{
this(dbObject, null, -1, false, null);
}
/**
* Create a DbProperty by supplying the DbObject and the property name. The
* value at time of creation will be populate automatically.
*
* @param dbObject
* @param propertyName
*/
public DbProperty(DbObject dbObject, String propertyName)
{
this(dbObject, propertyName, -1, false, null);
}
/**
* Create a DbProperty with an indexed value, e.g. for propertyName "myCollection" and
* index 4, the propertyName will be converted to "myCollection[4]" and the propertValue
* will be populated with the value at index 4 of myCollection.
*
* @param dbObject
* @param propertyName
* @param index
*/
public DbProperty(DbObject dbObject, String propertyName, int index)
{
this(dbObject, propertyName, index, false, null);
}
/**
* @return the dbObject
*/
public DbObject getDbObject()
{
return this.dbObject;
}
/**
* @return the propertyName
*/
public String getPropertyName()
{
return this.propertyName;
}
/**
* @return the propertyValue
*/
public Object getPropertyValue()
{
return this.propertyValue;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((this.dbObject == null) ? 0 : this.dbObject.hashCode());
result = prime * result + ((this.propertyName == null) ? 0 : this.propertyName.hashCode());
result = prime * result
+ ((this.propertyValue == null) ? 0 : this.propertyValue.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
DbProperty other = (DbProperty) obj;
if (this.dbObject == null)
{
if (other.dbObject != null) return false;
}
else if (!this.dbObject.equals(other.dbObject)) return false;
if (this.propertyName == null)
{
if (other.propertyName != null) return false;
}
else if (!this.propertyName.equals(other.propertyName)) return false;
if (this.propertyValue == null)
{
if (other.propertyValue != null) return false;
}
else if (!this.propertyValue.equals(other.propertyValue)) return false;
return true;
}
@Override
public String toString()
{
return "DbProperty [dbObject=" + this.dbObject + ", propertyName=" + this.propertyName
+ ", propertyValue=" + this.propertyValue + "]";
}
/**
* Work backwards from this DbProperty's DbObject to the root object to create a path in the
* following format:
* <p>
* root.child.grandchild[...].property
* <p>
* e.g. myschema.person.age.nullable
* <p>
* This isn't exactly the same as a FQ database object name, for example the property name could be indexed:
* <p>
* e.g. myschema.person.pk_person.columnNames[2]
* <p>
* to reflect the third column name in the primary key named "pk_person" on the person table.
*
* @return String path
*/
public String getPath()
{
StringBuffer sb = new StringBuffer();
if (getPropertyName() != null)
{
sb.append(".");
sb.append(getPropertyName());
}
for (DbObject pathElement = dbObject; pathElement != null; pathElement = pathElement.getParent())
{
sb.insert(0, pathElement.getName());
if (pathElement.getParent() != null)
{
sb.insert(0, ".");
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.schemacomp;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.alfresco.util.schemacomp.model.AbstractDbObject;
import org.alfresco.util.schemacomp.model.DbObject;
import org.junit.Test;
import org.mockito.Mockito;
public class DbPropertyTest
{
@Test(expected = IllegalArgumentException.class)
public void cannotHaveNullDbObject()
{
new DbProperty(null, "theProperty");
}
@Test
public void propertyValueCanBeRetrievedByReflection()
{
DbObjectWithIndexedProp dbo = Mockito.mock(DbObjectWithIndexedProp.class);
Mockito.when(dbo.getTheProperty()).thenReturn("This is the property value");
DbProperty dbProperty = new DbProperty(dbo, "theProperty");
assertEquals("This is the property value", dbProperty.getPropertyValue());
}
@Test
public void indexedPropertyValueCanBeRetrievedByReflection()
{
DbObjectWithIndexedProp dbo = Mockito.mock(DbObjectWithIndexedProp.class);
Mockito.when(dbo.getColours()).thenReturn(Arrays.asList("red", "green", "blue"));
DbProperty greenProperty = new DbProperty(dbo, "colours[1]");
DbProperty blueProperty = new DbProperty(dbo, "colours", 2);
assertEquals("green", greenProperty.getPropertyValue());
assertEquals("blue", blueProperty.getPropertyValue());
}
@Test
public void canGetPath()
{
new MyDbObject("root", 1).
add(new MyDbObject("child", 2)).
add(new MyDbObject("grandchild", 3)).
add(new MyDbObject("greatgrandchild", 4));
DbProperty levelProp = new DbProperty(MyDbObject.lastAdded, "level");
assertEquals("Incorrect path", "root.child.grandchild.greatgrandchild.level", levelProp.getPath());
DbProperty greatGrandChildProp = new DbProperty(MyDbObject.lastAdded);
assertEquals("Incorrect path", "root.child.grandchild.greatgrandchild", greatGrandChildProp.getPath());
}
private interface DbObjectWithIndexedProp extends DbObject
{
String getTheProperty();
List<String> getColours();
}
public static class MyDbObject extends AbstractDbObject
{
public static MyDbObject lastAdded;
private int level;
public MyDbObject(String name, int level)
{
super(null, name);
this.level = level;
}
@Override
public void accept(DbObjectVisitor visitor)
{
}
public MyDbObject add(MyDbObject child)
{
child.setParent(this);
lastAdded = child;
return child;
}
public int getLevel()
{
return this.level;
}
}
}

View File

@ -19,10 +19,11 @@
package org.alfresco.util.schemacomp;
import java.util.ArrayList;
import java.util.Collection;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Result.Where;
import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.model.DbObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
@ -54,42 +55,60 @@ public class DefaultComparisonUtils implements ComparisonUtils
@Override
public void compareSimpleCollections(Collection<? extends Object> leftCollection,
Collection<? extends Object> rightCollection, DiffContext ctx, Strength strength)
public void compareSimpleCollections(DbProperty leftProp,
DbProperty rightProp, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
for (Object leftObj : leftCollection)
@SuppressWarnings("unchecked")
Collection<? extends Object> leftCollection = (Collection<? extends Object>) leftProp.getPropertyValue();
@SuppressWarnings("unchecked")
Collection<? extends Object> rightCollection = (Collection<? extends Object>) rightProp.getPropertyValue();
// TODO: Temporary code during refactoring
ArrayList<? extends Object> leftList = new ArrayList<Object>(leftCollection);
ArrayList<? extends Object> rightList = new ArrayList<Object>(rightCollection);
Results differences = ctx.getDifferences();
for (int leftIndex = 0; leftIndex < leftList.size(); leftIndex++)
{
if (rightCollection.contains(leftObj))
Object leftObj = leftList.get(leftIndex);
DbProperty leftIndexedProp = new DbProperty(leftProp.getDbObject(), leftProp.getPropertyName(), leftIndex);
int rightIndex;
if ((rightIndex = rightList.indexOf(leftObj)) != -1)
{
// The same valued object in the right hand collection as in the left.
// Note: it isn't possible to determine a result of Where.IN_BOTH_BUT_DIFFERENCE
// with a 'simple' value as there is no way of knowing if the term represents the same value
// (e.g. two strings {red_value, green_value}, are these meant to be the same or different?)
differences.add(Where.IN_BOTH_NO_DIFFERENCE, leftObj, leftObj, strength);
DbProperty rightIndexedProp = new DbProperty(rightProp.getDbObject(), rightProp.getPropertyName(), rightIndex);
differences.add(Where.IN_BOTH_NO_DIFFERENCE, leftIndexedProp, rightIndexedProp, strength);
}
else
{
// No equivalent object in the right hand collection.
differences.add(Where.ONLY_IN_LEFT, leftObj, null, strength);
// Using rightIndexedProperty would result in index out of bounds error.
differences.add(Where.ONLY_IN_LEFT, leftIndexedProp, rightProp, strength);
}
}
// Identify objects in the right collection but not the left
for (Object rightObj : rightCollection)
for (int rightIndex = 0; rightIndex < rightList.size(); rightIndex++)
{
Object rightObj = rightList.get(rightIndex);
if (!leftCollection.contains(rightObj))
{
DbProperty rightIndexedProp = new DbProperty(rightProp.getDbObject(), rightProp.getPropertyName(), rightIndex);
// No equivalent object in the left hand collection.
differences.add(Where.ONLY_IN_RIGHT, null, rightObj, strength);
differences.add(Where.ONLY_IN_RIGHT, leftProp, rightIndexedProp, strength);
}
}
}
/**
* Compare collections, reporting differences using the default {@link Result.Strength}
* Compare collections, reporting differences using the default {@link Difference.Strength}
*
* @see #compareCollections(Collection, Collection, Differences, Strength)
* @see #compareCollections(Collection, Collection, Results, Strength)
*/
@Override
public void compareCollections(Collection<? extends DbObject> leftCollection,
@ -99,8 +118,8 @@ public class DefaultComparisonUtils implements ComparisonUtils
}
/**
* Compare collections of {@link DbObject}s using their {@link DbObject#diff(DbObject, Differences)} method.
* Differences are reported using the specified {@link Result.Strength}.
* Compare collections of {@link DbObject}s using their {@link DbObject#diff(DbObject, Results)} method.
* Differences are reported using the specified {@link Difference.Strength}.
*
* @param leftCollection
* @param rightCollection
@ -111,7 +130,7 @@ public class DefaultComparisonUtils implements ComparisonUtils
public void compareCollections(Collection<? extends DbObject> leftCollection,
Collection<? extends DbObject> rightCollection, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
Results differences = ctx.getDifferences();
for (DbObject leftObj : leftCollection)
{
DbObject rightObj = findSameObjectAs(rightCollection, leftObj);
@ -125,17 +144,19 @@ public class DefaultComparisonUtils implements ComparisonUtils
else
{
// No equivalent object in the right hand collection.
differences.add(Where.ONLY_IN_LEFT, leftObj, null, strength);
differences.add(Where.ONLY_IN_LEFT, new DbProperty(leftObj, null), null, strength);
}
}
// Identify objects in the right collection but not the left
for (DbObject rightObj : rightCollection)
{
if (!leftCollection.contains(rightObj))
DbObject leftObj = findSameObjectAs(leftCollection, rightObj);
if (leftObj == null)
{
// No equivalent object in the left hand collection.
differences.add(Where.ONLY_IN_RIGHT, null, rightObj, strength);
differences.add(Where.ONLY_IN_RIGHT, null, new DbProperty(rightObj, null), strength);
}
}
}
@ -143,10 +164,10 @@ public class DefaultComparisonUtils implements ComparisonUtils
/**
* Compare two simple objects. Differences are reported using the default Result.Strength.
*
* @see #compareSimple(Object, Object, Differences, Strength)
* @see #compareSimple(Object, Object, Results, Strength)
*/
@Override
public void compareSimple(Object left, Object right, DiffContext ctx)
public void compareSimple(DbProperty left, DbProperty right, DiffContext ctx)
{
compareSimple(left, right, ctx, null);
}
@ -161,11 +182,16 @@ public class DefaultComparisonUtils implements ComparisonUtils
* @param strength
*/
@Override
public void compareSimple(Object left, Object right, DiffContext ctx, Strength strength)
public void compareSimple(DbProperty leftProperty, DbProperty rightProperty, DiffContext ctx, Strength strength)
{
Where where = null;
Object left = leftProperty.getPropertyValue();
checkNotDbObject(left);
Object right = rightProperty.getPropertyValue();
checkNotDbObject(right);
if (left == right)
{
// Same object, or both nulls
@ -194,6 +220,19 @@ public class DefaultComparisonUtils implements ComparisonUtils
}
}
ctx.getDifferences().add(where, left, right, strength);
ctx.getDifferences().add(where, leftProperty, rightProperty, strength);
}
/**
* @param obj
*/
private void checkNotDbObject(Object obj)
{
if (obj != null && DbObject.class.isAssignableFrom(obj.getClass()))
{
throw new IllegalArgumentException(
"Property value is a DbObject - this method shouldn't be used to compare this type: " + obj);
}
}
}

View File

@ -30,8 +30,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Result.Where;
import org.alfresco.util.schemacomp.model.AbstractDbObject;
import org.alfresco.util.schemacomp.model.DbObject;
import org.hibernate.dialect.Dialect;
import org.junit.Before;
@ -48,7 +49,7 @@ import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class DefaultComparisonUtilsTest
{
private @Mock Differences differences;
private @Mock Results differences;
private DefaultComparisonUtils comparisonUtils;
private DiffContext ctx;
private @Mock Dialect dialect;
@ -63,22 +64,27 @@ public class DefaultComparisonUtilsTest
@Test
public void compareSimple()
{
comparisonUtils.compareSimple(null, null, ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, null, null, Strength.ERROR);
comparisonUtils.compareSimple(prop(null), prop(null), ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, prop(null), prop(null), Strength.ERROR);
comparisonUtils.compareSimple("not_null_string", "not_null_string", ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, "not_null_string", "not_null_string", Strength.ERROR);
comparisonUtils.compareSimple(prop("not_null_string"), prop("not_null_string"), ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, prop("not_null_string"), prop("not_null_string"), Strength.ERROR);
comparisonUtils.compareSimple("left", "right", ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_BUT_DIFFERENCE, "left", "right", Strength.ERROR);
comparisonUtils.compareSimple(prop("left"), prop("right"), ctx, Strength.ERROR);
verify(differences).add(Where.IN_BOTH_BUT_DIFFERENCE, prop("left"), prop("right"), Strength.ERROR);
comparisonUtils.compareSimple("left", null, ctx, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_LEFT, "left", null, Strength.ERROR);
comparisonUtils.compareSimple(prop("left"), prop(null), ctx, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_LEFT, prop("left"), prop(null), Strength.ERROR);
comparisonUtils.compareSimple(null, "right", ctx, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_RIGHT, null, "right", Strength.ERROR);
comparisonUtils.compareSimple(prop(null), prop("right"), ctx, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_RIGHT, prop(null), prop("right"), Strength.ERROR);
}
public DbProperty prop(String propValue)
{
DbObject dbo = new DbObjectWithCollection("dbo", null);
return dbPropForValue(dbo, "someProperty", propValue);
}
@Test
public void compareCollections()
@ -105,8 +111,8 @@ public class DefaultComparisonUtilsTest
verify(db4).diff(db4, ctx, Strength.ERROR);
// Objects in only one collections are marked as such
verify(differences).add(Where.ONLY_IN_LEFT, db2, null, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_RIGHT, null, db3, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_LEFT, new DbProperty(db2), null, Strength.ERROR);
verify(differences).add(Where.ONLY_IN_RIGHT, null, new DbProperty(db3), Strength.ERROR);
}
@ -124,6 +130,8 @@ public class DefaultComparisonUtilsTest
leftCollection.add(subCollectionLeft);
leftCollection.add(456);
leftCollection.add("left only");
DbObject leftDbObj = new DbObjectWithCollection("left", leftCollection);
DbProperty leftCollProp = new DbProperty(leftDbObj, "collection");
Collection<Object> rightCollection = new ArrayList<Object>();
rightCollection.add(123);
@ -133,20 +141,59 @@ public class DefaultComparisonUtilsTest
rightCollection.add("right only");
rightCollection.add("both");
rightCollection.add("one more right only");
DbObject rightDbObj = new DbObjectWithCollection("right", rightCollection);
DbProperty rightCollProp = new DbProperty(rightDbObj, "collection");
comparisonUtils.compareSimpleCollections(leftCollection, rightCollection, ctx, Strength.WARN);
comparisonUtils.compareSimpleCollections(leftCollProp, rightCollProp, ctx, Strength.WARN);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, 123, 123, Strength.WARN);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, "both", "both", Strength.WARN);
verify(differences).add(Where.IN_BOTH_NO_DIFFERENCE, subCollectionLeft, subCollectionRight, Strength.WARN);
verify(differences).add(Where.ONLY_IN_LEFT, 456, null, Strength.WARN);
verify(differences).add(Where.ONLY_IN_LEFT, "left only", null, Strength.WARN);
verify(differences).add(
Where.IN_BOTH_NO_DIFFERENCE,
dbPropForValue(leftDbObj, "collection[0]", 123),
dbPropForValue(rightDbObj, "collection[0]", 123),
Strength.WARN);
verify(differences).add(
Where.IN_BOTH_NO_DIFFERENCE,
dbPropForValue(leftDbObj, "collection[1]", "both"),
dbPropForValue(rightDbObj, "collection[4]", "both"),
Strength.WARN);
verify(differences).add(
Where.IN_BOTH_NO_DIFFERENCE,
dbPropForValue(leftDbObj, "collection[2]", subCollectionLeft),
dbPropForValue(rightDbObj, "collection[2]", subCollectionRight),
Strength.WARN);
verify(differences).add(
Where.ONLY_IN_LEFT,
dbPropForValue(leftDbObj, "collection[3]", 456),
dbPropForValue(rightDbObj, "collection", rightCollection),
Strength.WARN);
verify(differences).add(
Where.ONLY_IN_LEFT,
dbPropForValue(leftDbObj, "collection[4]", "left only"),
dbPropForValue(rightDbObj, "collection", rightCollection),
Strength.WARN);
verify(differences).add(Where.ONLY_IN_RIGHT, null, 789, Strength.WARN);
verify(differences).add(Where.ONLY_IN_RIGHT, null, "right only", Strength.WARN);
verify(differences).add(Where.ONLY_IN_RIGHT, null, "one more right only", Strength.WARN);
verify(differences).add(
Where.ONLY_IN_RIGHT,
dbPropForValue(leftDbObj, "collection", leftCollection),
dbPropForValue(rightDbObj, "collection[1]", 789),
Strength.WARN);
verify(differences).add(
Where.ONLY_IN_RIGHT,
dbPropForValue(leftDbObj, "collection", leftCollection),
dbPropForValue(rightDbObj, "collection[3]", "right only"),
Strength.WARN);
verify(differences).add(
Where.ONLY_IN_RIGHT,
dbPropForValue(leftDbObj, "collection", leftCollection),
dbPropForValue(rightDbObj, "collection[5]", "one more right only"),
Strength.WARN);
}
private DbProperty dbPropForValue(DbObject obj, String propName, Object propValue)
{
return new DbProperty(obj, propName, -1, true, propValue);
}
@Test
public void findSameObjectAsSuccessfulFind()
@ -194,4 +241,26 @@ public class DefaultComparisonUtilsTest
}
return dbObjects;
}
public static class DbObjectWithCollection extends AbstractDbObject
{
private Collection<Object> collection;
public DbObjectWithCollection(String name, Collection<Object> collection)
{
super(null, name);
this.collection = collection;
}
@Override
public void accept(DbObjectVisitor visitor)
{
}
public Collection<Object> getCollection()
{
return this.collection;
}
}
}

View File

@ -25,21 +25,21 @@ import org.hibernate.dialect.Dialect;
/**
* A context made available to schema differencing and validation operations. It supplies information
* about the {@link Dialect database dialect} that should be used when validating database properties
* and the {@link Differences} object that should be populated with schema differences and validation errors.
* and the {@link Results} object that should be populated with schema differences and validation errors.
*
* @author Matt Ward
*/
public class DiffContext
{
private final Dialect dialect;
private final Differences differences;
private final Results differences;
private final List<ValidationResult> validationResults;
/**
* @param dialect
* @param differences
*/
public DiffContext(Dialect dialect, Differences differences, List<ValidationResult> validationResults)
public DiffContext(Dialect dialect, Results differences, List<ValidationResult> validationResults)
{
this.dialect = dialect;
this.differences = differences;
@ -57,7 +57,7 @@ public class DiffContext
/**
* @return the differences
*/
public Differences getDifferences()
public Results getDifferences()
{
return this.differences;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.schemacomp;
/**
* Result of a comparison between two database objects.
*
* @author Matt Ward
*/
public final class Difference extends Result
{
/** Specifies the type of differences */
public enum Where { ONLY_IN_LEFT, ONLY_IN_RIGHT, IN_BOTH_NO_DIFFERENCE, IN_BOTH_BUT_DIFFERENCE };
private final Where where;
private final DbProperty left;
private final DbProperty right;
public Difference(Where where, DbProperty left, DbProperty right)
{
this(where, left, right, null);
}
public Difference(Where where, DbProperty left, DbProperty right, Strength strength)
{
super(null);
this.where = where;
this.left = left;
this.right = right;
}
/**
* @return the where
*/
public Where getWhere()
{
return this.where;
}
/**
* @return the left
*/
public DbProperty getLeft()
{
return this.left;
}
/**
* @return the right
*/
public DbProperty getRight()
{
return this.right;
}
@Override
public String toString()
{
return "Difference [where=" + this.where + ", left=" + this.left + ", right=" + this.right
+ ", strength=" + this.strength + "]";
}
}

View File

@ -1,138 +0,0 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.schemacomp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Result.Where;
import org.alfresco.util.schemacomp.model.DbObject;
import org.apache.commons.lang.StringUtils;
/**
* Collects differences so that tools can report on or respond to differences between database schemas.
*
* @author Matt Ward
*/
public class Differences implements Iterable<Result>
{
private final List<Result> items = new ArrayList<Result>();
private final Path path = new Path();
/**
* @see Path
*/
public void pushPath(String component)
{
this.path.push(component);
}
/**
* @see Path
*/
public String popPath()
{
return this.path.pop();
}
/**
* Record a difference between two objects, or specify that an object only appears in either the
* 'left' or 'right' schemas.
*
* @param where The type of difference, see {@link Where}
* @param left Left value, or null if the item appears in the right, but not left schema.
* @param right Right value, or null if the item appears in the left, but not right schema.
* @param strength The Result.Strength of the difference, e.g. WARN or ERROR.
*/
public void add(Where where, Object left, Object right, Strength strength)
{
Result result = new Result(where, left, right, path.getCurrentPath(), strength);
items.add(result);
}
public void add(Where where, Object left, Object right)
{
add(where, left, right, null);
}
/**
* Obtain an iterator for the top-level items held in this schema - since this is a hierarchical model,
* deeper items are obtained by navigating through the top-level items.
*/
@Override
public Iterator<Result> iterator()
{
return items.iterator();
}
/**
* @return How many top-level items are in the schema.
*/
public int size()
{
return items.size();
}
/**
* Specifies where in a database schema a difference occurs - this is largely the same as the fully qualified
* name of a database object, e.g. <code>public.alf_node</code> except that if an object doesn't have a name
* then a suitable label will be provided for that path element.
* <p>
* Elements can be <code>push</code>ed onto the path and <code>pop</code>ped from the path, making it easier for a
* {@link DbObject} to process that object's data as part of a heirachy - the parent object should push an
* appropriate label onto the path for itself, before invoking any child objects'
* {@link DbObject#diff(DbObject, Differences)} method.
*
* @author Matt Ward
*/
private static class Path
{
private Stack<String> components = new Stack<String>();
private String current;
public void push(String component)
{
components.push(component);
makeCurrentPath();
}
public String pop()
{
String component = components.pop();
makeCurrentPath();
return component;
}
public String getCurrentPath()
{
return current;
}
private void makeCurrentPath()
{
current = StringUtils.join(components, ".");
}
}
}

View File

@ -18,77 +18,24 @@
*/
package org.alfresco.util.schemacomp;
/**
* Result of a comparison between two database objects.
*
* Base class for the result of a differencing or validation operation.
*
* @author Matt Ward
*/
public final class Result
public class Result
{
/** Specifies the type of differences */
public enum Where { ONLY_IN_LEFT, ONLY_IN_RIGHT, IN_BOTH_NO_DIFFERENCE, IN_BOTH_BUT_DIFFERENCE };
public enum Strength { WARN, ERROR };
private final Where where;
private final Object left;
private final Object right;
private final String path;
private final Strength strength;
protected final Strength strength;
public Result(Where where, Object left, Object right, String path)
/**
* @param strength
*/
public Result(Strength strength)
{
this(where, left, right, path, null);
}
public Result(Where where, Object left, Object right, String path, Strength strength)
{
this.where = where;
this.left = left;
this.right = right;
this.path = path;
this.strength = (strength != null ? strength : Strength.ERROR);
}
/**
* @return the where
*/
public Where getWhere()
{
return this.where;
}
/**
* @return the left
*/
public Object getLeft()
{
return this.left;
}
/**
* @return the right
*/
public Object getRight()
{
return this.right;
}
/**
* @return the path
*/
public String getPath()
{
return this.path;
}
/**
* @return the strength
*/
@ -96,13 +43,4 @@ public final class Result
{
return this.strength;
}
@Override
public String toString()
{
return "Result [where=" + this.where + ", left=" + this.left + ", right=" + this.right
+ ", path=" + this.path + ", strength=" + this.strength + "]";
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.util.schemacomp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.Result.Strength;
/**
* Collects differences so that tools can report on or respond to differences between database schemas.
*
* @author Matt Ward
*/
public class Results implements Iterable<Difference>
{
private final List<Difference> items = new ArrayList<Difference>();
/** Temporary step during refactor - Where.IN_BOTH_NO_DIFFERENCE will be going altogether */
private boolean reportNonDifferences = false;
/**
* Record a difference between two objects, or specify that an object only appears in either the
* 'left' or 'right' schemas.
*
* @param where The type of difference, see {@link Where}
* @param left Left value, or null if the item appears in the right, but not left schema.
* @param right Right value, or null if the item appears in the left, but not right schema.
* @param strength The Result.Strength of the difference, e.g. WARN or ERROR.
*/
public void add(Where where, DbProperty left, DbProperty right, Strength strength)
{
if (where != Where.IN_BOTH_NO_DIFFERENCE || reportNonDifferences)
{
Difference result = new Difference(where, left, right, strength);
items.add(result);
}
}
public void add(Where where, DbProperty left, DbProperty right)
{
add(where, left, right, null);
}
/**
* Obtain an iterator for the top-level items held in this schema - since this is a hierarchical model,
* deeper items are obtained by navigating through the top-level items.
*/
@Override
public Iterator<Difference> iterator()
{
return items.iterator();
}
/**
* @return How many top-level items are in the schema.
*/
public int size()
{
return items.size();
}
}

View File

@ -29,6 +29,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses(
{
DbPropertyTest.class,
DefaultComparisonUtilsTest.class,
SchemaComparatorTest.class,
ValidatingVisitorTest.class

View File

@ -47,7 +47,7 @@ public class SchemaComparator
{
this.leftSchema = left;
this.rightSchema = right;
this.ctx = new DiffContext(dialect, new Differences(), new ArrayList<ValidationResult>());
this.ctx = new DiffContext(dialect, new Results(), new ArrayList<ValidationResult>());
}
@ -81,7 +81,7 @@ public class SchemaComparator
/**
* @return the differences
*/
public Differences getDifferences()
public Results getDifferences()
{
return ctx.getDifferences();
}

View File

@ -20,7 +20,7 @@ package org.alfresco.util.schemacomp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
@ -28,10 +28,8 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Result.Where;
import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.model.Column;
import org.alfresco.util.schemacomp.model.DbObject;
import org.alfresco.util.schemacomp.model.ForeignKey;
import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.PrimaryKey;
@ -68,18 +66,18 @@ public class SchemaComparatorTest
public void canPerformDiff()
{
// Left hand side's database objects.
left.add(new Table("tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
left.add(new Table(left, "tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
pk("pk_tbl_no_diff", "id"), fkeys(fk("fk_tbl_no_diff", "nodeRef", "node", "nodeRef")),
indexes("idx_node id nodeRef")));
left.add(table("table_in_left"));
left.add(new Table("tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
left.add(new Table(left, "tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
pk("pk_is_diff", "id"), fkeys(), indexes()));
// Right hand side's database objects.
right.add(new Table("tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
right.add(new Table(right, "tbl_no_diff", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)", "name VARCHAR2(150)"),
pk("pk_tbl_no_diff", "id"), fkeys(fk("fk_tbl_no_diff", "nodeRef", "node", "nodeRef")),
indexes("idx_node id nodeRef")));
right.add(new Table("tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
right.add(new Table(right, "tbl_has_diff_pk", columns("id NUMBER(10)", "nodeRef VARCHAR2(200)"),
pk("pk_is_diff", "nodeRef"), fkeys(), indexes()));
right.add(table("table_in_right"));
@ -87,53 +85,57 @@ public class SchemaComparatorTest
comparator = new SchemaComparator(left, right, dialect);
comparator.validateAndCompare();
// See stdout for diagnostics dump...
dumpDiffs(comparator.getDifferences(), false);
dumpValidation(comparator.getValidationResults());
Iterator<Result> it = comparator.getDifferences().iterator();
assertHasDifference("left_schema", "left_schema", "right_schema", it.next()); // schema names
assertNoDifference("left_schema.tbl_no_diff", "tbl_no_diff", it.next());
assertNoDifference("left_schema.tbl_no_diff.id", "id", it.next());
assertNoDifference("left_schema.tbl_no_diff.id", "NUMBER(10)", it.next());
assertNoDifference("left_schema.tbl_no_diff.id", Boolean.FALSE, it.next()); // nullable
assertNoDifference("left_schema.tbl_no_diff.nodeRef", "nodeRef", it.next());
assertNoDifference("left_schema.tbl_no_diff.nodeRef", "VARCHAR2(200)", it.next());
assertNoDifference("left_schema.tbl_no_diff.nodeRef", Boolean.FALSE, it.next()); // nullable
assertNoDifference("left_schema.tbl_no_diff.name", "name", it.next());
assertNoDifference("left_schema.tbl_no_diff.name", "VARCHAR2(150)", it.next());
assertNoDifference("left_schema.tbl_no_diff.name", Boolean.FALSE, it.next()); // nullable
assertNoDifference("left_schema.tbl_no_diff.pk_tbl_no_diff", "pk_tbl_no_diff", it.next()); // name field
assertNoDifference("left_schema.tbl_no_diff.pk_tbl_no_diff", "id", it.next()); // first (& only) column of list
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "fk_tbl_no_diff", it.next()); // name field
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "nodeRef", it.next()); // localColumn
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "node", it.next()); // targetTable
assertNoDifference("left_schema.tbl_no_diff.fk_tbl_no_diff", "nodeRef", it.next()); // targetColumn
assertNoDifference("left_schema.tbl_no_diff.idx_node", "idx_node", it.next()); // index name
assertNoDifference("left_schema.tbl_no_diff.idx_node", "id", it.next()); // first indexed column
assertNoDifference("left_schema.tbl_no_diff.idx_node", "nodeRef", it.next()); // second indexed column
// TODO: why are diffs for table not flattened out as for index?
assertOnlyInOne("left_schema", Where.ONLY_IN_LEFT, table("table_in_left"), it.next());
assertNoDifference("left_schema.tbl_has_diff_pk", "tbl_has_diff_pk", it.next());
assertNoDifference("left_schema.tbl_has_diff_pk.id", "id", it.next());
assertNoDifference("left_schema.tbl_has_diff_pk.id", "NUMBER(10)", it.next());
assertNoDifference("left_schema.tbl_has_diff_pk.id", Boolean.FALSE, it.next()); // nullable
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", "nodeRef", it.next());
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", "VARCHAR2(200)", it.next());
assertNoDifference("left_schema.tbl_has_diff_pk.nodeRef", Boolean.FALSE, it.next()); // nullable
assertNoDifference("left_schema.tbl_has_diff_pk.pk_is_diff", "pk_is_diff", it.next()); // name field
// TODO: surely this should be a diff rather than a ONLY_IN_LEFT plus ONLY_IN_RIGHT?
// assertHasDifference("left_schema.tbl_has_diff_pk.pk_is_diff", "id", "nodeRef", it.next()); // first (& only) column of list
assertOnlyInOne("left_schema.tbl_has_diff_pk.pk_is_diff", Where.ONLY_IN_LEFT, "id", it.next()); // first (& only) column of list
Results differences = comparator.getDifferences();
assertEquals(5, differences.size());
// This belong to the pk_is_diff above.
assertOnlyInOne("left_schema.tbl_has_diff_pk.pk_is_diff", Where.ONLY_IN_RIGHT, "nodeRef", it.next()); // first (& only) column of list
Iterator<Difference> it = differences.iterator();
// Items that are ONLY_IN_RIGHT always come at the end
assertEquals("Should be table with correct name", "tbl_has_diff_pk", ((DbObject) it.next().getRight()).getName());
assertOnlyInOne("left_schema", Where.ONLY_IN_RIGHT, table("table_in_right"), it.next());
// Schema names are different ("left_schema" vs "right_schema")
Difference diff = it.next();
assertEquals(Where.IN_BOTH_BUT_DIFFERENCE, diff.getWhere());
assertEquals("left_schema.name", diff.getLeft().getPath());
assertEquals("right_schema.name", diff.getRight().getPath());
assertSame(left, diff.getLeft().getDbObject());
assertSame(right, diff.getRight().getDbObject());
assertEquals("name", diff.getLeft().getPropertyName());
assertEquals("left_schema", diff.getLeft().getPropertyValue());
assertEquals("name", diff.getRight().getPropertyName());
assertEquals("right_schema", diff.getRight().getPropertyValue());
// Table table_in_left only appears in the left schema
diff = it.next();
assertEquals(Where.ONLY_IN_LEFT, diff.getWhere());
assertEquals("left_schema.table_in_left", diff.getLeft().getPath());
assertEquals(null, diff.getRight());
assertEquals(null, diff.getLeft().getPropertyName());
assertEquals(null, diff.getLeft().getPropertyValue());
// Table tbl_has_diff_pk has PK of "id" in left and "nodeRef" in right
diff = it.next();
assertEquals(Where.ONLY_IN_LEFT, diff.getWhere());
assertEquals("left_schema.tbl_has_diff_pk.pk_is_diff.columnNames[0]", diff.getLeft().getPath());
assertEquals("right_schema.tbl_has_diff_pk.pk_is_diff.columnNames", diff.getRight().getPath());
assertEquals("columnNames[0]", diff.getLeft().getPropertyName());
assertEquals("id", diff.getLeft().getPropertyValue());
assertEquals("columnNames", diff.getRight().getPropertyName());
assertEquals(Arrays.asList("nodeRef"), diff.getRight().getPropertyValue());
// Table tbl_has_diff_pk has PK of "id" in left and "nodeRef" in right
diff = it.next();
assertEquals(Where.ONLY_IN_RIGHT, diff.getWhere());
assertEquals("left_schema.tbl_has_diff_pk.pk_is_diff.columnNames", diff.getLeft().getPath());
assertEquals("right_schema.tbl_has_diff_pk.pk_is_diff.columnNames[0]", diff.getRight().getPath());
assertEquals("columnNames", diff.getLeft().getPropertyName());
assertEquals(Arrays.asList("id"), diff.getLeft().getPropertyValue());
assertEquals("columnNames[0]", diff.getRight().getPropertyName());
assertEquals("nodeRef", diff.getRight().getPropertyValue());
// Table table_in_right does not exist in the left schema
}
@ -145,111 +147,11 @@ public class SchemaComparatorTest
System.out.println(r);
}
}
@Test
public void canReportWarnings()
{
// Left hand side's database objects.
left.add(new Table("tbl_example", columns("id NUMBER(10)"), pk("pk_tbl_example", "id"), fkeys(),
indexes("idx_specified_name id")));
// Right hand side's database objects.
right.add(new Table("tbl_example", columns("id NUMBER(10)"), pk("pk_tbl_example", "id"), fkeys(),
indexes("sys_random_idx_name id")));
comparator = new SchemaComparator(left, right, dialect);
comparator.validateAndCompare();
dumpDiffs(comparator.getDifferences(), true);
dumpValidation(comparator.getValidationResults());
Iterator<Result> it = comparator.getDifferences().iterator();
assertHasDifference("left_schema", "left_schema", "right_schema", it.next());
assertNoDifference("left_schema.tbl_example", "tbl_example", it.next());
assertNoDifference("left_schema.tbl_example.id", "id", it.next());
assertNoDifference("left_schema.tbl_example.id", "NUMBER(10)", it.next());
assertNoDifference("left_schema.tbl_example.id", Boolean.FALSE, it.next());
assertNoDifference("left_schema.tbl_example.pk_tbl_example", "pk_tbl_example", it.next());
assertNoDifference("left_schema.tbl_example.pk_tbl_example", "id", it.next());
assertHasWarning(
"left_schema.tbl_example.idx_specified_name",
"idx_specified_name",
"sys_random_idx_name",
it.next());
}
/**
* Assert that the result shows the value to have different values in the left and right items.
*/
private void assertHasDifference(String path, Object leftValue, Object rightValue,
Result result, Strength strength)
{
assertEquals(strength, result.getStrength());
assertEquals(Where.IN_BOTH_BUT_DIFFERENCE, result.getWhere());
assertEquals(path, result.getPath());
assertEquals(leftValue, result.getLeft());
assertEquals(rightValue, result.getRight());
}
/**
* @see #assertHasDifference(String, Object, Object, Result, Strength)
*/
private void assertHasDifference(String path, Object leftValue, Object rightValue, Result result)
{
assertHasDifference(path, leftValue, rightValue, result, Strength.ERROR);
}
/**
* @see #assertHasDifference(String, Object, Object, Result, Strength)
*/
private void assertHasWarning(String path, Object leftValue, Object rightValue, Result result)
{
assertHasDifference(path, leftValue, rightValue, result, Strength.WARN);
}
/**
* Assert that the result shows the value to be present only in either the left or right items.
*/
private void assertOnlyInOne(String path, Where which, Object value, Result result)
{
assertEquals(which, result.getWhere());
assertEquals(path, result.getPath());
if (which == Where.ONLY_IN_LEFT)
{
assertEquals(value, result.getLeft());
assertNull(result.getRight());
}
else if (which == Where.ONLY_IN_RIGHT)
{
assertNull(result.getLeft());
assertEquals(value, result.getRight());
}
else
{
throw new IllegalArgumentException("The 'which' argument should be ONLY_IN_LEFT or ONLY_IN_RIGHT.");
}
}
/**
* Assert that the result shows no differences between the left and right items.
*/
private void assertNoDifference(String path, Object value, Result result)
{
assertEquals(Where.IN_BOTH_NO_DIFFERENCE, result.getWhere());
assertEquals(path, result.getPath());
assertEquals(value, result.getLeft());
assertEquals(value, result.getRight());
}
private void dumpDiffs(Differences differences, boolean showNonDifferences)
private void dumpDiffs(Results differences, boolean showNonDifferences)
{
System.out.println("Differences (" + differences.size() + ")");
for (Result d : differences)
for (Difference d : differences)
{
if (d.getWhere() != Where.IN_BOTH_NO_DIFFERENCE || showNonDifferences)
{
@ -260,7 +162,7 @@ public class SchemaComparatorTest
private Table table(String name)
{
return new Table(name, columns("id NUMBER(10)"), pk("pk_" + name, "id"), fkeys(), indexes());
return new Table(null, name, columns("id NUMBER(10)"), pk("pk_" + name, "id"), fkeys(), indexes());
}
private Collection<Column> columns(String... colDefs)
@ -271,7 +173,7 @@ public class SchemaComparatorTest
for (int i = 0; i < colDefs.length; i++)
{
String[] parts = colDefs[i].split(" ");
columns[i] = new Column(parts[0], parts[1], false);
columns[i] = new Column(null, parts[0], parts[1], false);
}
return Arrays.asList(columns);
}
@ -279,7 +181,7 @@ public class SchemaComparatorTest
private PrimaryKey pk(String name, String... columnNames)
{
assertTrue("No columns specified", columnNames.length > 0);
PrimaryKey pk = new PrimaryKey(name, Arrays.asList(columnNames));
PrimaryKey pk = new PrimaryKey(null, name, Arrays.asList(columnNames));
return pk;
}
@ -290,7 +192,7 @@ public class SchemaComparatorTest
private ForeignKey fk(String fkName, String localColumn, String targetTable, String targetColumn)
{
return new ForeignKey(fkName, localColumn, targetTable, targetColumn);
return new ForeignKey(null, fkName, localColumn, targetTable, targetColumn);
}
private Collection<Index> indexes(String... indexDefs)
@ -301,7 +203,7 @@ public class SchemaComparatorTest
String[] parts = indexDefs[i].split(" ");
String name = parts[0];
String[] columns = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
indexes[i] = new Index(name, Arrays.asList(columns));
indexes[i] = new Index(null, name, Arrays.asList(columns));
}
return Arrays.asList(indexes);
}

View File

@ -19,10 +19,9 @@
package org.alfresco.util.schemacomp;
import java.util.ArrayList;
import java.util.Collections;
import static org.junit.Assert.assertSame;
import javax.faces.validator.Validator;
import java.util.ArrayList;
import org.alfresco.util.schemacomp.model.Column;
import org.alfresco.util.schemacomp.model.ForeignKey;
@ -33,17 +32,14 @@ import org.alfresco.util.schemacomp.model.Sequence;
import org.alfresco.util.schemacomp.model.Table;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.alfresco.util.schemacomp.validator.NameValidator;
import org.alfresco.util.schemacomp.validator.NullValidator;
import org.hibernate.dialect.MySQL5InnoDBDialect;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.*;
import static org.mockito.Mockito.validateMockitoUsage;
/**
* TODO: comment me!
* Tests for the ValidatingVisitor class.
*
* @author Matt Ward
*/
public class ValidatingVisitorTest
@ -54,7 +50,7 @@ public class ValidatingVisitorTest
@Before
public void setUp() throws Exception
{
ctx = new DiffContext(new MySQL5InnoDBDialect(), new Differences(), new ArrayList<ValidationResult>());
ctx = new DiffContext(new MySQL5InnoDBDialect(), new Results(), new ArrayList<ValidationResult>());
visitor = new ValidatingVisitor(ctx);
}
@ -78,7 +74,7 @@ public class ValidatingVisitorTest
public void canValidate()
{
visitor.indexNameValidator = Mockito.mock(NameValidator.class);
Index index = new Index("index_name", new ArrayList<String>());
Index index = new Index(null, "index_name", new ArrayList<String>());
visitor.visit(index);

View File

@ -19,26 +19,48 @@
package org.alfresco.util.schemacomp;
/**
* TODO: comment me!
* Results of a validation operation.
*
* @author Matt Ward
*/
public class ValidationResult
public class ValidationResult extends Result
{
private Object value;
private DbProperty dbProperty;
/**
* @param value
*/
public ValidationResult(Object value)
public ValidationResult(DbProperty dbProperty)
{
this.value = value;
this(dbProperty, null);
}
public ValidationResult(DbProperty dbProperty, Strength strength)
{
super(strength);
this.dbProperty = dbProperty;
}
/**
* @return the dbProperty that was rejected.
*/
public DbProperty getDbProperty()
{
return this.dbProperty;
}
/**
* @return the value
* @param dbProperty the dbProperty to set
*/
public void setDbProperty(DbProperty dbProperty)
{
this.dbProperty = dbProperty;
}
/**
* @return the value that was rejected.
*/
public Object getValue()
{
return this.value;
return this.dbProperty.getPropertyValue();
}
}

View File

@ -18,17 +18,12 @@
*/
package org.alfresco.util.schemacomp.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.alfresco.util.schemacomp.ComparisonUtils;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DefaultComparisonUtils;
import org.springframework.util.StringUtils;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Results;
/**
* Useful base class for many, if not all the {@link DbObject} implementations.
@ -37,25 +32,22 @@ import org.springframework.util.StringUtils;
*/
public abstract class AbstractDbObject implements DbObject
{
private DbObject parent;
private String name;
/** How differences in the name field should be reported */
private Strength nameStrength = Strength.ERROR;
protected ComparisonUtils comparisonUtils = new DefaultComparisonUtils();
/**
* Default constructor
*/
public AbstractDbObject()
{
}
/**
* Instantiate, giving the object a name.
* Instantiate, giving the object a parent and a name.
*
* @param parent
* @param name
*/
public AbstractDbObject(String name)
public AbstractDbObject(DbObject parent, String name)
{
this.parent = parent;
this.name = name;
}
@ -152,8 +144,8 @@ public abstract class AbstractDbObject implements DbObject
}
/**
* Provides an implementation of {@link DbObject#diff(DbObject, Differences)}. The template
* method {@link #doDiff(DbObject, Differences)} provides the subclass specific diffing logic,
* Provides an implementation of {@link DbObject#diff(DbObject, Results)}. The template
* method {@link #doDiff(DbObject, Results)} provides the subclass specific diffing logic,
* whilst this method handles the workflow required in most cases: set the path's prefix that will be
* used to explain where differences occur; compare the name fields of the two objects; delegate to the
* subclass specific diffing (if any); remove the last path addition ready for the next object to perform
@ -161,23 +153,30 @@ public abstract class AbstractDbObject implements DbObject
*/
@Override
public void diff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
{
DbProperty leftNameProp = new DbProperty(this, "name");
DbProperty rightNameProp = new DbProperty(right, "name");
comparisonUtils.compareSimple(leftNameProp, rightNameProp, ctx, getNameStrength());
if (name != null && StringUtils.hasText(name))
{
differences.pushPath(name);
}
else
{
differences.pushPath("<" + getClass().getSimpleName() + ">");
}
comparisonUtils.compareSimple(name, right.getName(), ctx, getNameStrength());
doDiff(right, ctx, strength);
differences.popPath();
}
@Override
public DbObject getParent()
{
return parent;
}
@Override
public void setParent(DbObject parent)
{
this.parent = parent;
}
/**
* Override this method to provide subclass specific diffing logic.
*

View File

@ -26,10 +26,11 @@ import static org.mockito.Mockito.inOrder;
import java.util.ArrayList;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Result.Where;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.ValidationResult;
import org.hibernate.dialect.Dialect;
import org.junit.Before;
@ -48,7 +49,7 @@ import org.mockito.runners.MockitoJUnitRunner;
public class AbstractDbObjectTest
{
private ConcreteDbObject dbObject;
private @Mock Differences differences;
private @Mock Results differences;
private DiffContext ctx;
private @Mock Dialect dialect;
@ -93,37 +94,52 @@ public class AbstractDbObjectTest
dbObject.diff(otherObject, ctx, Strength.ERROR);
InOrder inOrder = inOrder(differences);
// The name of the object should be pushed on to the differences path.
inOrder.verify(differences).pushPath("the_object");
// The name of the object should be diffed
inOrder.verify(differences).add(Where.IN_BOTH_BUT_DIFFERENCE, "the_object", "the_other_object", Strength.WARN);
inOrder.verify(differences).add(
Where.IN_BOTH_BUT_DIFFERENCE,
new DbProperty(dbObject, "name"),
new DbProperty(otherObject, "name"),
Strength.WARN);
// Then the doDiff() method should be processed
inOrder.verify(differences).add(Where.IN_BOTH_BUT_DIFFERENCE, "left", "right");
// Later, the path should be popped again
inOrder.verify(differences).popPath();
inOrder.verify(differences).add(
Where.IN_BOTH_BUT_DIFFERENCE,
new DbProperty(dbObject, "someProp"),
new DbProperty(otherObject, "someProp"));
}
/**
* Concrete DbObject for testing the AbstractDbObject base class.
*/
private static class ConcreteDbObject extends AbstractDbObject
public static class ConcreteDbObject extends AbstractDbObject
{
private String someProp = "property value";
public ConcreteDbObject(String name)
{
super(name);
super(null, name);
}
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
differences.add(Where.IN_BOTH_BUT_DIFFERENCE, "left", "right");
Results differences = ctx.getDifferences();
differences.add(
Where.IN_BOTH_BUT_DIFFERENCE,
new DbProperty(this, "someProp"),
new DbProperty(right, "someProp"));
}
@Override
public void accept(DbObjectVisitor visitor)
{
}
public String getSomeProp()
{
return this.someProp;
}
}
}

View File

@ -19,8 +19,9 @@
package org.alfresco.util.schemacomp.model;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -37,13 +38,14 @@ public class Column extends AbstractDbObject
/**
* Construct a Column.
*
* @table the parent table
* @param name
* @param type
* @param nullable
*/
public Column(String name, String type, boolean nullable)
public Column(Table table, String name, String type, boolean nullable)
{
super(name);
super(table, name);
this.type = type;
this.nullable = nullable;
}
@ -109,10 +111,16 @@ public class Column extends AbstractDbObject
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
Column rightColumn = (Column) right;
comparisonUtils.compareSimple(type, rightColumn.type, ctx);
comparisonUtils.compareSimple(nullable, rightColumn.nullable, ctx);
Results differences = ctx.getDifferences();
DbProperty thisTypeProp = new DbProperty(this, "type");
DbProperty thisNullableProp = new DbProperty(this, "nullable");
Column thatColumn = (Column) right;
DbProperty thatTypeProp = new DbProperty(thatColumn, "type");
DbProperty thatNullableProp = new DbProperty(thatColumn, "nullable");
comparisonUtils.compareSimple(thisTypeProp, thatTypeProp, ctx);
comparisonUtils.compareSimple(thisNullableProp, thatNullableProp, ctx);
}
@Override

View File

@ -19,6 +19,7 @@
package org.alfresco.util.schemacomp.model;
import org.alfresco.util.schemacomp.DbProperty;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;
@ -36,8 +37,8 @@ public class ColumnTest extends DbObjectTestBase<Column>
@Before
public void setUp() throws Exception
{
thisColumn = new Column("this_column", "VARCHAR2(100)", false);
thatColumn = new Column("this_column", "NUMBER(10)", true);
thisColumn = new Column(null, "this_column", "VARCHAR2(100)", false);
thatColumn = new Column(null, "that_column", "NUMBER(10)", true);
}
@Override
@ -55,8 +56,14 @@ public class ColumnTest extends DbObjectTestBase<Column>
@Override
protected void doDiffTests()
{
inOrder.verify(comparisonUtils).compareSimple(thisColumn.getType(), thatColumn.getType(), ctx);
inOrder.verify(comparisonUtils).compareSimple(thisColumn.isNullable(), thatColumn.isNullable(), ctx);
DbProperty thisTypeProp = new DbProperty(thisColumn, "type");
DbProperty thatTypeProp = new DbProperty(thatColumn, "type");
inOrder.verify(comparisonUtils).compareSimple(thisTypeProp, thatTypeProp, ctx);
DbProperty thisNullableProp = new DbProperty(thisColumn, "nullable");
DbProperty thatNullableProp = new DbProperty(thatColumn, "nullable");
inOrder.verify(comparisonUtils).compareSimple(thisNullableProp, thatNullableProp, ctx);
}
@Test

View File

@ -20,7 +20,7 @@ package org.alfresco.util.schemacomp.model;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -54,7 +54,7 @@ public interface DbObject
/**
* Generate a report of differences between this object ('left') and another object ('right').
* Differences between the left and right objects under inspection are captured in the {@link Differences}
* Differences between the left and right objects under inspection are captured in the {@link Results}
* object passed in to this method.
*
* @param right The object to compare against.
@ -70,4 +70,20 @@ public interface DbObject
* @param visitor
*/
void accept(DbObjectVisitor visitor);
/**
* Get the parent object for which this object is a child. If this is the root object
* then null should be returned.
*
* @return Parent reference or null
*/
DbObject getParent();
/**
* Sets the parent object.
*
* @see #getParent()
* @param parent
*/
void setParent(DbObject parent);
}

View File

@ -25,8 +25,9 @@ import java.util.List;
import org.alfresco.util.schemacomp.ComparisonUtils;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.ValidationResult;
import org.hibernate.dialect.Dialect;
@ -46,7 +47,7 @@ import org.mockito.runners.MockitoJUnitRunner;
public abstract class DbObjectTestBase<T extends AbstractDbObject>
{
protected @Mock Dialect dialect;
protected @Mock Differences differences;
protected @Mock Results differences;
protected DiffContext ctx;
protected @Mock ComparisonUtils comparisonUtils;
protected InOrder inOrder;
@ -89,21 +90,15 @@ public abstract class DbObjectTestBase<T extends AbstractDbObject>
// Invoke the method under test
thisObject.diff(thatObject, ctx, Strength.ERROR);
// The name of the object should be pushed on to the differences path.
inOrder.verify(differences).pushPath(thisObject.getName());
// The name of the object should be diffed
inOrder.verify(comparisonUtils).compareSimple(
thisObject.getName(),
thatObject.getName(),
new DbProperty(thisObject, "name"),
new DbProperty(thatObject, "name"),
ctx,
thisObject.getNameStrength());
// Then the doDiff() method should be processed...
doDiffTests();
// Later, the path should be popped again
inOrder.verify(differences).popPath();
}
protected abstract void doDiffTests();

View File

@ -19,8 +19,9 @@
package org.alfresco.util.schemacomp.model;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength;
@ -40,14 +41,15 @@ public class ForeignKey extends AbstractDbObject
/**
* Constructor.
*
* @param table the parent table
* @param fkName
* @param localColumn
* @param targetTable
* @param targetColumn
*/
public ForeignKey(String fkName, String localColumn, String targetTable, String targetColumn)
public ForeignKey(Table table, String fkName, String localColumn, String targetTable, String targetColumn)
{
super(fkName);
super(table, fkName);
this.localColumn = localColumn;
this.targetTable = targetTable;
this.targetColumn = targetColumn;
@ -141,11 +143,19 @@ public class ForeignKey extends AbstractDbObject
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
ForeignKey rightFK = (ForeignKey) right;
comparisonUtils.compareSimple(localColumn, rightFK.localColumn, ctx);
comparisonUtils.compareSimple(targetTable, rightFK.targetTable, ctx);
comparisonUtils.compareSimple(targetColumn, rightFK.targetColumn, ctx);
ForeignKey thatFK = (ForeignKey) right;
comparisonUtils.compareSimple(
new DbProperty(this, "localColumn"),
new DbProperty(thatFK, "localColumn"),
ctx);
comparisonUtils.compareSimple(
new DbProperty(this, "targetTable"),
new DbProperty(thatFK, "targetTable"),
ctx);
comparisonUtils.compareSimple(
new DbProperty(this, "targetColumn"),
new DbProperty(thatFK, "targetColumn"),
ctx);
}
@Override

View File

@ -21,6 +21,7 @@ package org.alfresco.util.schemacomp.model;
import static org.mockito.Mockito.verify;
import org.alfresco.util.schemacomp.DbProperty;
import org.junit.Before;
import org.junit.Test;
@ -37,8 +38,8 @@ public class ForeignKeyTest extends DbObjectTestBase<ForeignKey>
@Before
public void setUp() throws Exception
{
thisFK = new ForeignKey("this_fk", "local_col", "target_table", "target_col");
thatFK = new ForeignKey("that_fk", "local_col", "target_table", "target_col");
thisFK = new ForeignKey(null, "this_fk", "local_col", "target_table", "target_col");
thatFK = new ForeignKey(null, "that_fk", "local_col", "target_table", "target_col");
}
@ -59,9 +60,18 @@ public class ForeignKeyTest extends DbObjectTestBase<ForeignKey>
@Override
protected void doDiffTests()
{
inOrder.verify(comparisonUtils).compareSimple(thisFK.getLocalColumn(), thatFK.getLocalColumn(), ctx);
inOrder.verify(comparisonUtils).compareSimple(thisFK.getTargetTable(), thatFK.getTargetTable(), ctx);
inOrder.verify(comparisonUtils).compareSimple(thisFK.getTargetColumn(), thatFK.getTargetColumn(), ctx);
inOrder.verify(comparisonUtils).compareSimple(
new DbProperty(thisFK, "localColumn"),
new DbProperty(thatFK, "localColumn"),
ctx);
inOrder.verify(comparisonUtils).compareSimple(
new DbProperty(thisFK, "targetTable"),
new DbProperty(thatFK, "targetTable"),
ctx);
inOrder.verify(comparisonUtils).compareSimple(
new DbProperty(thisFK, "targetColumn"),
new DbProperty(thatFK, "targetColumn"),
ctx);
}
@Test

View File

@ -21,8 +21,8 @@ package org.alfresco.util.schemacomp.model;
import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -36,11 +36,12 @@ public class Index extends AbstractDbObject
/**
* @param table the parent table
* @param columnNames
*/
public Index(String name, List<String> columnNames)
public Index(Table table, String name, List<String> columnNames)
{
super(name);
super(table, name);
this.columnNames = columnNames;
setNameStrength(Strength.WARN);
}
@ -112,9 +113,12 @@ public class Index extends AbstractDbObject
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
Index rightIndex = (Index) right;
comparisonUtils.compareSimpleCollections(columnNames, rightIndex.columnNames, ctx, strength);
comparisonUtils.compareSimpleCollections(
new DbProperty(this, "columnNames"),
new DbProperty(rightIndex, "columnNames"),
ctx,
strength);
}

View File

@ -20,6 +20,7 @@ package org.alfresco.util.schemacomp.model;
import java.util.Arrays;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.Result.Strength;
import org.junit.Before;
import org.junit.Test;
@ -39,8 +40,8 @@ public class IndexTest extends DbObjectTestBase<Index>
@Before
public void setUp()
{
thisIndex = new Index("this_index", Arrays.asList("id", "name", "age"));
thatIndex = new Index("that_index", Arrays.asList("a", "b"));
thisIndex = new Index(null, "this_index", Arrays.asList("id", "name", "age"));
thatIndex = new Index(null, "that_index", Arrays.asList("a", "b"));
}
@Override
@ -59,8 +60,8 @@ public class IndexTest extends DbObjectTestBase<Index>
protected void doDiffTests()
{
inOrder.verify(comparisonUtils).compareSimpleCollections(
thisIndex.getColumnNames(),
thatIndex.getColumnNames(),
new DbProperty(thisIndex, "columnNames"),
new DbProperty(thatIndex, "columnNames"),
ctx,
Strength.ERROR);
}
@ -77,19 +78,19 @@ public class IndexTest extends DbObjectTestBase<Index>
public void sameAs()
{
assertTrue("Indexes should be logically the same.",
thisIndex.sameAs(new Index("this_index", Arrays.asList("id", "name", "age"))));
thisIndex.sameAs(new Index(null, "this_index", Arrays.asList("id", "name", "age"))));
assertTrue("Indexes should be logically the same, despite different names (as same column order)",
thisIndex.sameAs(new Index("different_name", Arrays.asList("id", "name", "age"))));
thisIndex.sameAs(new Index(null, "different_name", Arrays.asList("id", "name", "age"))));
assertTrue("Indexes should be identified as the same despite different column order (as same name).",
thisIndex.sameAs(new Index("this_index", Arrays.asList("name", "id", "age"))));
thisIndex.sameAs(new Index(null, "this_index", Arrays.asList("name", "id", "age"))));
assertFalse("Indexes should be identified different (different name and column order)",
thisIndex.sameAs(new Index("different_name", Arrays.asList("name", "id", "age"))));
thisIndex.sameAs(new Index(null, "different_name", Arrays.asList("name", "id", "age"))));
assertFalse("Indexes should be identified different (different name & different columns)",
thisIndex.sameAs(new Index("different_name", Arrays.asList("node_ref", "url"))));
thisIndex.sameAs(new Index(null, "different_name", Arrays.asList("node_ref", "url"))));
}

View File

@ -21,8 +21,8 @@ package org.alfresco.util.schemacomp.model;
import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -37,12 +37,13 @@ public class PrimaryKey extends AbstractDbObject
/**
* Constructor
* @param table the parent table
* @param name
* @param columnNames
*/
public PrimaryKey(String name, List<String> columnNames)
public PrimaryKey(Table table, String name, List<String> columnNames)
{
super(name);
super(table, name);
this.columnNames = columnNames;
}
@ -89,9 +90,12 @@ public class PrimaryKey extends AbstractDbObject
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
PrimaryKey rightPK = (PrimaryKey) right;
comparisonUtils.compareSimpleCollections(columnNames, rightPK.columnNames, ctx, strength);
comparisonUtils.compareSimpleCollections(
new DbProperty(this, "columnNames"),
new DbProperty(rightPK, "columnNames"),
ctx,
strength);
}
@Override

View File

@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify;
import java.util.Arrays;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.Result.Strength;
import org.junit.Before;
import org.junit.Test;
@ -40,8 +41,8 @@ public class PrimaryKeyTest extends DbObjectTestBase<PrimaryKey>
@Before
public void setUp()
{
thisPK = new PrimaryKey("this_pk", Arrays.asList("id", "name", "age"));
thatPK = new PrimaryKey("that_pk", Arrays.asList("a", "b"));
thisPK = new PrimaryKey(null, "this_pk", Arrays.asList("id", "name", "age"));
thatPK = new PrimaryKey(null, "that_pk", Arrays.asList("a", "b"));
}
@Override
@ -60,8 +61,8 @@ public class PrimaryKeyTest extends DbObjectTestBase<PrimaryKey>
protected void doDiffTests()
{
inOrder.verify(comparisonUtils).compareSimpleCollections(
thisPK.getColumnNames(),
thatPK.getColumnNames(),
new DbProperty(thisPK, "columnNames"),
new DbProperty(thatPK, "columnNames"),
ctx,
Strength.ERROR);
}

View File

@ -24,7 +24,7 @@ import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -43,11 +43,18 @@ public class Schema extends AbstractDbObject implements Iterable<DbObject>
*/
public Schema(String name)
{
super(name);
super(null, name);
}
/**
* Add an object to this schema - this method will set this schema
* as the object's parent.
*
* @param dbObject
*/
public void add(DbObject dbObject)
{
dbObject.setParent(this);
objects.add(dbObject);
}
@ -94,7 +101,6 @@ public class Schema extends AbstractDbObject implements Iterable<DbObject>
@Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
Schema rightSchema = (Schema) right;
comparisonUtils.compareCollections(objects, rightSchema.objects, ctx);
}

View File

@ -28,9 +28,9 @@ import org.alfresco.util.schemacomp.DbObjectVisitor;
*/
public class Sequence extends AbstractDbObject
{
public Sequence(String name)
public Sequence(DbObject parent, String name)
{
super(name);
super(parent, name);
}
@Override

View File

@ -21,7 +21,6 @@ package org.alfresco.util.schemacomp.model;
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
@ -36,8 +35,8 @@ public class SequenceTest extends DbObjectTestBase<Sequence>
@Before
public void setUp()
{
thisSequence = new Sequence("this_sequence");
thatSequence = new Sequence("that_sequence");
thisSequence = new Sequence(null, "this_sequence");
thatSequence = new Sequence(null, "that_sequence");
}
@Test

View File

@ -24,7 +24,6 @@ import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Result.Strength;
/**
@ -34,28 +33,29 @@ import org.alfresco.util.schemacomp.Result.Strength;
*/
public class Table extends AbstractDbObject
{
private List<Column> columns = new ArrayList<Column>();
private final List<Column> columns = new ArrayList<Column>();
private PrimaryKey primaryKey;
private List<ForeignKey> foreignKeys = new ArrayList<ForeignKey>();
private List<Index> indexes = new ArrayList<Index>();
private final List<ForeignKey> foreignKeys = new ArrayList<ForeignKey>();
private final List<Index> indexes = new ArrayList<Index>();
public Table(String name, Collection<Column> columns, PrimaryKey primaryKey,
public Table(Schema parentSchema, String name, Collection<Column> columns, PrimaryKey primaryKey,
Collection<ForeignKey> foreignKeys, Collection<Index> indexes)
{
super(name);
super(parentSchema, name);
if (columns != null)
{
this.columns.addAll(columns);
setColumns(columns);
}
primaryKey.setParent(this);
this.primaryKey = primaryKey;
if (foreignKeys != null)
{
this.foreignKeys.addAll(foreignKeys);
setForeignKeys(foreignKeys);
}
if (indexes != null)
{
this.indexes.addAll(indexes);
setIndexes(indexes);
}
}
@ -72,9 +72,15 @@ public class Table extends AbstractDbObject
/**
* @param columns the columns to set
*/
public void setColumns(List<Column> columns)
public void setColumns(Collection<Column> columns)
{
this.columns = columns;
this.columns.clear();
this.columns.addAll(columns);
for (Column column : columns)
{
column.setParent(this);
}
}
@ -92,6 +98,7 @@ public class Table extends AbstractDbObject
*/
public void setPrimaryKey(PrimaryKey primaryKey)
{
primaryKey.setParent(this);
this.primaryKey = primaryKey;
}
@ -108,9 +115,15 @@ public class Table extends AbstractDbObject
/**
* @param foreignKeys the foreignKeys to set
*/
public void setForeignKeys(List<ForeignKey> foreignKeys)
public void setForeignKeys(Collection<ForeignKey> foreignKeys)
{
this.foreignKeys = foreignKeys;
this.foreignKeys.clear();
this.foreignKeys.addAll(foreignKeys);
for (ForeignKey fk : foreignKeys)
{
fk.setParent(this);
}
}
@ -126,9 +139,15 @@ public class Table extends AbstractDbObject
/**
* @param indexes the indexes to set
*/
public void setIndexes(List<Index> indexes)
public void setIndexes(Collection<Index> indexes)
{
this.indexes = indexes;
this.indexes.clear();
this.indexes.addAll(indexes);
for (Index index : indexes)
{
index.setParent(this);
}
}
@ -179,7 +198,6 @@ public class Table extends AbstractDbObject
@Override
protected void doDiff(DbObject other, DiffContext ctx, Strength strength)
{
Differences differences = ctx.getDifferences();
Table rightTable = (Table) other;
comparisonUtils.compareCollections(columns, rightTable.columns, ctx);
primaryKey.diff(rightTable.primaryKey, ctx, strength);

View File

@ -19,17 +19,12 @@
package org.alfresco.util.schemacomp.model;
import static java.util.Arrays.asList;
import static org.mockito.Mockito.verify;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.alfresco.util.schemacomp.Result.Strength;
import org.apache.poi.ss.formula.functions.Columns;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -56,26 +51,11 @@ public class TableTest extends DbObjectTestBase<Table>
@Before
public void setUp() throws Exception
{
/*columns = asList(
new Column("columnA", "VARCHAR2(50)", false),
new Column("columnB", "VARCHAR2(100)", false),
new Column("columnC", "VARCHAR2(200)", true));
foreignKeys = asList(new ForeignKey("fk", "localcolumn", "targettable", "targetcolumn"));
indexes = asList(new Index("an_index", asList("columnA", "columnC")));
table = new Table("the_table", columns, primaryKey, foreignKeys, indexes);
otherTable = new Table("the_other_table", columns, primaryKey, foreignKeys, indexes);*/
columns = listOfMocks(Column.class, 3);
foreignKeys = listOfMocks(ForeignKey.class, 1);
indexes = listOfMocks(Index.class, 1);
table = new Table("the_table", columns, primaryKey, foreignKeys, indexes);
otherTable = new Table("the_other_table", columns, primaryKey, foreignKeys, indexes);
table = new Table(null, "the_table", columns, primaryKey, foreignKeys, indexes);
otherTable = new Table(null, "the_other_table", columns, primaryKey, foreignKeys, indexes);
}

View File

@ -22,7 +22,9 @@ import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.model.DbObject;
/**
* TODO: comment me!
* DbObject validators must implement this interface. DbValidator instances
* are used by the ValidatingVisitor class.
*
* @author Matt Ward
*/
public interface DbValidator

View File

@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.ValidationResult;
import org.alfresco.util.schemacomp.model.DbObject;
@ -47,7 +48,7 @@ public class NameValidator implements DbValidator
Pattern pattern = namePatterns.get(ctx.getDialect().getClass());
ValidationResult result = new ValidationResult(name);
ValidationResult result = new ValidationResult(new DbProperty(dbo, "name"));
if (pattern != null && !pattern.matcher(name).matches())
{

View File

@ -19,15 +19,16 @@
package org.alfresco.util.schemacomp.validator;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Differences;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.ValidationResult;
import org.alfresco.util.schemacomp.model.DbObject;
import org.alfresco.util.schemacomp.model.Index;
@ -36,8 +37,6 @@ import org.hibernate.dialect.Oracle10gDialect;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Tests for the NameValidator class.
*
@ -54,7 +53,7 @@ public class NameValidatorTest
{
validator = new NameValidator();
validationResults = new ArrayList<ValidationResult>();
ctx = new DiffContext(new Oracle10gDialect(), new Differences(), validationResults);
ctx = new DiffContext(new Oracle10gDialect(), new Results(), validationResults);
}
@Test
@ -88,6 +87,6 @@ public class NameValidatorTest
private DbObject indexForName(String name)
{
return new Index(name, new ArrayList<String>());
return new Index(null, name, new ArrayList<String>());
}
}

View File

@ -22,7 +22,8 @@ import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.model.DbObject;
/**
* TODO: comment me!
* A 'do nothing' validator
*
* @author Matt Ward
*/
public class NullValidator implements DbValidator