ALF-10771: schema validation and differences rules

Validation serialized and deserialized in XML.



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31942 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Matt Ward
2011-11-14 16:25:49 +00:00
parent 6b3eb6181f
commit 3df35a9d06
15 changed files with 432 additions and 62 deletions

View File

@@ -19,6 +19,7 @@
package org.alfresco.util.schemacomp; package org.alfresco.util.schemacomp;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.alfresco.util.schemacomp.model.Column; import org.alfresco.util.schemacomp.model.Column;
import org.alfresco.util.schemacomp.model.DbObject; import org.alfresco.util.schemacomp.model.DbObject;
@@ -27,6 +28,8 @@ import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.PrimaryKey; import org.alfresco.util.schemacomp.model.PrimaryKey;
import org.alfresco.util.schemacomp.model.Schema; import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Table; import org.alfresco.util.schemacomp.model.Table;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.apache.poi.hssf.record.DVALRecord;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@@ -65,21 +68,62 @@ public class DbObjectXMLTransformer
// All DbObjects result in an XML element with the DbObject's class name as the tag // All DbObjects result in an XML element with the DbObject's class name as the tag
// and a name attribute corresponding to the value of getName(), // and a name attribute corresponding to the value of getName(),
// e.g. For an instance of a Column: <column name="the_column_name"/> // e.g. For an instance of a Column: <column name="the_column_name"/>
String tagName = dbObject.getClass().getSimpleName().toLowerCase();
final AttributesImpl attribs = new AttributesImpl(); final AttributesImpl attribs = new AttributesImpl();
attribs.addAttribute("", "", "name", "CDATA", dbObject.getName()); attribs.addAttribute("", "", XML.ATTR_NAME, "CDATA", dbObject.getName());
// Add class-specific attributes. // Add class-specific attributes.
addAttributes(dbObject, attribs); addAttributes(dbObject, attribs);
String tagName = dbObject.getClass().getSimpleName().toLowerCase();
xmlOut.startElement("", "", tagName, attribs); xmlOut.startElement("", "", tagName, attribs);
// The element's contents can optionally be populated with class-specific content. // The element's contents can optionally be populated with class-specific content.
transformDbObject(dbObject); transformDbObject(dbObject);
// All DbObjects potentially have validator configuration present in the XML.
transformValidators(dbObject.getValidators());
// Provide the end tag, or close an empty element. // Provide the end tag, or close an empty element.
xmlOut.endElement("", "", tagName); xmlOut.endElement("", "", tagName);
} }
/**
* @param validators
* @throws SAXException
*/
private void transformValidators(List<DbValidator<? extends DbObject>> validators) throws SAXException
{
if (validators.size() > 0)
{
simpleStartTag(XML.EL_VALIDATORS);
for (DbValidator<? extends DbObject> dbv : validators)
{
final AttributesImpl attribs = new AttributesImpl();
attribs.addAttribute("", "", XML.ATTR_CLASS, "CDATA", dbv.getClass().getName());
xmlOut.startElement("", "", XML.EL_VALIDATOR, attribs);
if (dbv.getPropertyNames().size() > 0)
{
simpleStartTag(XML.EL_PROPERTIES);
for (String propName : dbv.getPropertyNames())
{
final AttributesImpl propAttrs = new AttributesImpl();
propAttrs.addAttribute("", "", XML.ATTR_NAME, "CDATA", propName);
xmlOut.startElement("", "", XML.EL_PROPERTY, propAttrs);
String propValue = dbv.getProperty(propName);
char[] chars = propValue.toCharArray();
xmlOut.characters(chars, 0, chars.length);
simpleEndTag(XML.EL_PROPERTY);
}
simpleEndTag(XML.EL_PROPERTIES);
}
simpleEndTag(XML.EL_VALIDATOR);
}
simpleEndTag(XML.EL_VALIDATORS);
}
}
/** /**
* Add class-specific attributes. * Add class-specific attributes.
* *
@@ -91,7 +135,7 @@ public class DbObjectXMLTransformer
if (dbObject instanceof Index) if (dbObject instanceof Index)
{ {
Index index = (Index) dbObject; Index index = (Index) dbObject;
attribs.addAttribute("", "", "unique", "CDATA", Boolean.toString(index.isUnique())); attribs.addAttribute("", "", XML.ATTR_UNIQUE, "CDATA", Boolean.toString(index.isUnique()));
} }
} }
@@ -127,55 +171,55 @@ public class DbObjectXMLTransformer
private void transformSchema(Schema schema) throws SAXException private void transformSchema(Schema schema) throws SAXException
{ {
simpleStartTag("objects"); simpleStartTag(XML.EL_OBJECTS);
for (DbObject dbo : schema) for (DbObject dbo : schema)
{ {
output(dbo); output(dbo);
} }
simpleEndTag("objects"); simpleEndTag(XML.EL_OBJECTS);
} }
private void transformTable(Table table) throws SAXException private void transformTable(Table table) throws SAXException
{ {
// Output columns // Output columns
simpleStartTag("columns"); simpleStartTag(XML.EL_COLUMNS);
for (Column column : table.getColumns()) for (Column column : table.getColumns())
{ {
output(column); output(column);
} }
simpleEndTag("columns"); simpleEndTag(XML.EL_COLUMNS);
// Output primary key // Output primary key
output(table.getPrimaryKey()); output(table.getPrimaryKey());
// Output foreign keys // Output foreign keys
simpleStartTag("foreignkeys"); simpleStartTag(XML.EL_FOREIGN_KEYS);
for (ForeignKey fk : table.getForeignKeys()) for (ForeignKey fk : table.getForeignKeys())
{ {
output(fk); output(fk);
} }
simpleEndTag("foreignkeys"); simpleEndTag(XML.EL_FOREIGN_KEYS);
// Output indexes // Output indexes
simpleStartTag("indexes"); simpleStartTag(XML.EL_INDEXES);
for (Index index : table.getIndexes()) for (Index index : table.getIndexes())
{ {
output(index); output(index);
} }
simpleEndTag("indexes"); simpleEndTag(XML.EL_INDEXES);
} }
private void transformColumn(Column column) throws SAXException private void transformColumn(Column column) throws SAXException
{ {
simpleElement("type", column.getType()); simpleElement(XML.EL_TYPE, column.getType());
simpleElement("nullable", Boolean.toString(column.isNullable())); simpleElement(XML.EL_NULLABLE, Boolean.toString(column.isNullable()));
} }
private void transformForeignKey(ForeignKey fk) throws SAXException private void transformForeignKey(ForeignKey fk) throws SAXException
{ {
simpleElement("localcolumn", fk.getLocalColumn()); simpleElement(XML.EL_LOCAL_COLUMN, fk.getLocalColumn());
simpleElement("targettable", fk.getTargetTable()); simpleElement(XML.EL_TARGET_TABLE, fk.getTargetTable());
simpleElement("targetcolumn", fk.getTargetColumn()); simpleElement(XML.EL_TARGET_COLUMN, fk.getTargetColumn());
} }
private void transformIndex(Index index) throws SAXException private void transformIndex(Index index) throws SAXException
@@ -219,11 +263,11 @@ public class DbObjectXMLTransformer
private void columnNameList(Collection<String> columnNames) throws SAXException private void columnNameList(Collection<String> columnNames) throws SAXException
{ {
simpleStartTag("columnnames"); simpleStartTag(XML.EL_COLUMN_NAMES);
for (String columnName : columnNames) for (String columnName : columnNames)
{ {
simpleElement("columnname", columnName); simpleElement(XML.EL_COLUMN_NAME, columnName);
} }
simpleEndTag("columnnames"); simpleEndTag(XML.EL_COLUMN_NAMES);
} }
} }

View File

@@ -31,8 +31,12 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import javax.xml.transform.OutputKeys; import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer; import javax.xml.transform.Transformer;
@@ -43,12 +47,15 @@ import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import org.alfresco.util.schemacomp.model.Column; 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.ForeignKey;
import org.alfresco.util.schemacomp.model.Index; import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.PrimaryKey; import org.alfresco.util.schemacomp.model.PrimaryKey;
import org.alfresco.util.schemacomp.model.Schema; import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Sequence; import org.alfresco.util.schemacomp.model.Sequence;
import org.alfresco.util.schemacomp.model.Table; import org.alfresco.util.schemacomp.model.Table;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.alfresco.util.schemacomp.validator.NameValidator;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -248,6 +255,53 @@ public class DbObjectXMLTransformerTest
assertEquals("</table>", reader.readLine()); assertEquals("</table>", reader.readLine());
} }
@Test
public void transformObjectWithValidators() throws IOException
{
Collection<Column> columns = columns("one VARCHAR2(100)", "two NUMBER(10)");
PrimaryKey pk = new PrimaryKey(null, "pk_for_my_table", Arrays.asList("id"));
Collection<ForeignKey> fks = fkeys(fk("fk_one", "lc", "tt", "tc"), fk("fk_two", "lc", "tt", "tc"));
Collection<Index> indexes = indexes("index_one col1 col2", "index_two col3 col4");
Table table = new Table(null, "my_table", columns, pk, fks, indexes);
NameValidator nameValidator = new NameValidator();
nameValidator.setPattern(Pattern.compile("match_me_if_you_can"));
List<DbValidator<? extends DbObject>> validators = new ArrayList<DbValidator<? extends DbObject>>();
validators.add(nameValidator);
table.setValidators(validators);
transformer.output(table);
BufferedReader reader = new BufferedReader(new StringReader(writer.toString()));
dumpOutput();
assertHasPreamble(reader);
assertEquals("<table name=\"my_table\">", reader.readLine());
assertEquals(" <columns>", reader.readLine());
skipUntilEnd(" {column}", reader);
skipUntilEnd(" {column}", reader);
assertEquals(" </columns>", reader.readLine());
skipUntilEnd(" {primarykey}", reader);
assertEquals(" <foreignkeys>", reader.readLine());
skipUntilEnd(" {foreignkey}", reader);
skipUntilEnd(" {foreignkey}", reader);
assertEquals(" </foreignkeys>", reader.readLine());
assertEquals(" <indexes>", reader.readLine());
skipUntilEnd(" {index}", reader);
skipUntilEnd(" {index}", reader);
assertEquals(" </indexes>", reader.readLine());
assertEquals(" <validators>", reader.readLine());
assertEquals(" <validator class=\"org.alfresco.util.schemacomp.validator.NameValidator\">", reader.readLine());
assertEquals(" <properties>", reader.readLine());
assertEquals(" <property name=\"pattern\">match_me_if_you_can</property>", reader.readLine());
assertEquals(" </properties>", reader.readLine());
assertEquals(" </validator>", reader.readLine());
assertEquals(" </validators>", reader.readLine());
assertEquals("</table>", reader.readLine());
}
/** /**
* Ignore lines that are tested elsewhere, e.g. ignore serialized Column objects * Ignore lines that are tested elsewhere, e.g. ignore serialized Column objects
* in the context of a Table since we only need to know that there was text for a * in the context of a Table since we only need to know that there was text for a

View File

@@ -30,9 +30,12 @@ public abstract class XML
public static final String EL_SCHEMA = Schema.class.getSimpleName().toLowerCase(); public static final String EL_SCHEMA = Schema.class.getSimpleName().toLowerCase();
public static final String EL_TABLE = Table.class.getSimpleName().toLowerCase(); public static final String EL_TABLE = Table.class.getSimpleName().toLowerCase();
public static final String EL_COLUMN = Column.class.getSimpleName().toLowerCase(); public static final String EL_COLUMN = Column.class.getSimpleName().toLowerCase();
public static final String EL_COLUMNS = "columns";
public static final String EL_PRIMARY_KEY = PrimaryKey.class.getSimpleName().toLowerCase(); public static final String EL_PRIMARY_KEY = PrimaryKey.class.getSimpleName().toLowerCase();
public static final String EL_FOREIGN_KEY = ForeignKey.class.getSimpleName().toLowerCase(); public static final String EL_FOREIGN_KEY = ForeignKey.class.getSimpleName().toLowerCase();
public static final String EL_FOREIGN_KEYS = "foreignkeys";
public static final String EL_INDEX = Index.class.getSimpleName().toLowerCase(); public static final String EL_INDEX = Index.class.getSimpleName().toLowerCase();
public static final String EL_INDEXES = "indexes";
public static final String EL_SEQUENCE = Sequence.class.getSimpleName().toLowerCase(); public static final String EL_SEQUENCE = Sequence.class.getSimpleName().toLowerCase();
public static final String EL_TYPE = "type"; public static final String EL_TYPE = "type";
public static final String EL_NULLABLE = "nullable"; public static final String EL_NULLABLE = "nullable";
@@ -41,7 +44,13 @@ public abstract class XML
public static final String EL_LOCAL_COLUMN = "localcolumn"; public static final String EL_LOCAL_COLUMN = "localcolumn";
public static final String EL_TARGET_COLUMN = "targetcolumn"; public static final String EL_TARGET_COLUMN = "targetcolumn";
public static final String EL_TARGET_TABLE = "targettable"; public static final String EL_TARGET_TABLE = "targettable";
public static final String EL_VALIDATORS = "validators";
public static final String EL_VALIDATOR = "validator";
public static final String EL_OBJECTS = "objects";
public static final String EL_PROPERTIES = "properties";
public static final String EL_PROPERTY = "property";
public static final String ATTR_NAME = "name"; public static final String ATTR_NAME = "name";
public static final String ATTR_UNIQUE = "unique"; public static final String ATTR_UNIQUE = "unique";
public static final String ATTR_CLASS = "class";
} }

View File

@@ -20,6 +20,8 @@ package org.alfresco.util.schemacomp;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack; import java.util.Stack;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
@@ -33,6 +35,7 @@ import org.alfresco.util.schemacomp.model.PrimaryKey;
import org.alfresco.util.schemacomp.model.Schema; import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Sequence; import org.alfresco.util.schemacomp.model.Sequence;
import org.alfresco.util.schemacomp.model.Table; import org.alfresco.util.schemacomp.model.Table;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
@@ -47,7 +50,7 @@ public class XMLToSchema extends DefaultHandler
private SAXParser parser; private SAXParser parser;
private InputStream in; private InputStream in;
private Schema schema; private Schema schema;
private Stack<DbObject> dboStack = new Stack<DbObject>(); private Stack<Object> stack = new Stack<Object>();
private String lastTag; private String lastTag;
private String lastText; private String lastText;
@@ -97,43 +100,53 @@ public class XMLToSchema extends DefaultHandler
{ {
if (qName.equals(XML.EL_TABLE)) if (qName.equals(XML.EL_TABLE))
{ {
Table table = (Table) dboStack.pop(); Table table = (Table) stack.pop();
schema.add(table); schema.add(table);
} }
else if (qName.equals(XML.EL_COLUMN)) else if (qName.equals(XML.EL_COLUMN))
{ {
Column column = (Column) dboStack.pop(); Column column = (Column) stack.pop();
Table table = (Table) dboStack.peek(); Table table = (Table) stack.peek();
table.getColumns().add(column); table.getColumns().add(column);
} }
else if (qName.equals(XML.EL_PRIMARY_KEY)) else if (qName.equals(XML.EL_PRIMARY_KEY))
{ {
PrimaryKey pk = (PrimaryKey) dboStack.pop(); PrimaryKey pk = (PrimaryKey) stack.pop();
Table table = (Table) dboStack.peek(); Table table = (Table) stack.peek();
table.setPrimaryKey(pk); table.setPrimaryKey(pk);
} }
else if (qName.equals(XML.EL_FOREIGN_KEY)) else if (qName.equals(XML.EL_FOREIGN_KEY))
{ {
ForeignKey fk = (ForeignKey) dboStack.pop(); ForeignKey fk = (ForeignKey) stack.pop();
Table table = (Table) dboStack.peek(); Table table = (Table) stack.peek();
table.getForeignKeys().add(fk); table.getForeignKeys().add(fk);
} }
else if (qName.equals(XML.EL_INDEX)) else if (qName.equals(XML.EL_INDEX))
{ {
Index index = (Index) dboStack.pop(); Index index = (Index) stack.pop();
Table table = (Table) dboStack.peek(); Table table = (Table) stack.peek();
table.getIndexes().add(index); table.getIndexes().add(index);
} }
else if (qName.equals(XML.EL_SEQUENCE)) else if (qName.equals(XML.EL_SEQUENCE))
{ {
Sequence seq = (Sequence) dboStack.pop(); Sequence seq = (Sequence) stack.pop();
schema.add(seq); schema.add(seq);
} }
else if (qName.equals(XML.EL_VALIDATOR))
{
@SuppressWarnings("unchecked")
DbValidator<? extends DbObject> validator = (DbValidator<? extends DbObject>) stack.pop();
DbObject dbo = (DbObject) stack.peek();
dbo.getValidators().add(validator);
}
else if (qName.equals(XML.EL_PROPERTY))
{
//stack.pop();
}
} }
@SuppressWarnings("unchecked")
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes atts) public void startElement(String uri, String localName, String qName, Attributes atts)
throws SAXException throws SAXException
@@ -146,30 +159,50 @@ public class XMLToSchema extends DefaultHandler
} }
else if (qName.equals(XML.EL_TABLE)) else if (qName.equals(XML.EL_TABLE))
{ {
dboStack.push(new Table(atts.getValue(XML.ATTR_NAME))); stack.push(new Table(atts.getValue(XML.ATTR_NAME)));
} }
else if (qName.equals(XML.EL_COLUMN)) else if (qName.equals(XML.EL_COLUMN))
{ {
dboStack.push(new Column(atts.getValue(XML.ATTR_NAME))); stack.push(new Column(atts.getValue(XML.ATTR_NAME)));
} }
else if (qName.equals(XML.EL_PRIMARY_KEY)) else if (qName.equals(XML.EL_PRIMARY_KEY))
{ {
dboStack.push(new PrimaryKey(atts.getValue(XML.ATTR_NAME))); stack.push(new PrimaryKey(atts.getValue(XML.ATTR_NAME)));
} }
else if (qName.equals(XML.EL_FOREIGN_KEY)) else if (qName.equals(XML.EL_FOREIGN_KEY))
{ {
dboStack.push(new ForeignKey(atts.getValue(XML.ATTR_NAME))); stack.push(new ForeignKey(atts.getValue(XML.ATTR_NAME)));
} }
else if (qName.equals(XML.EL_INDEX)) else if (qName.equals(XML.EL_INDEX))
{ {
Index index = new Index(atts.getValue(XML.ATTR_NAME)); Index index = new Index(atts.getValue(XML.ATTR_NAME));
boolean unique = Boolean.parseBoolean(atts.getValue(XML.ATTR_UNIQUE)); boolean unique = Boolean.parseBoolean(atts.getValue(XML.ATTR_UNIQUE));
index.setUnique(unique); index.setUnique(unique);
dboStack.push(index); stack.push(index);
} }
else if (qName.equals(XML.EL_SEQUENCE)) else if (qName.equals(XML.EL_SEQUENCE))
{ {
dboStack.push(new Sequence(atts.getValue(XML.ATTR_NAME))); stack.push(new Sequence(atts.getValue(XML.ATTR_NAME)));
}
else if (qName.equals(XML.EL_VALIDATOR))
{
String className = atts.getValue(XML.ATTR_CLASS);
DbValidator<? extends DbObject> validator = null;
try
{
validator = (DbValidator<? extends DbObject>) Class.forName(className).newInstance();
}
catch (Throwable e)
{
throw new RuntimeException("Couldn't create validator, class: " + className, e);
}
stack.push(validator);
}
else if (qName.equals(XML.EL_PROPERTY))
{
String name = atts.getValue(XML.ATTR_NAME);
stack.push(name);
} }
} }
@@ -182,42 +215,53 @@ public class XMLToSchema extends DefaultHandler
{ {
if (lastTag.equals(XML.EL_TYPE)) if (lastTag.equals(XML.EL_TYPE))
{ {
Column column = (Column) dboStack.peek(); Column column = (Column) stack.peek();
column.setType(lastText); column.setType(lastText);
} }
else if (lastTag.equals(XML.EL_NULLABLE)) else if (lastTag.equals(XML.EL_NULLABLE))
{ {
Column column = (Column) dboStack.peek(); Column column = (Column) stack.peek();
column.setNullable(Boolean.parseBoolean(lastText)); column.setNullable(Boolean.parseBoolean(lastText));
} }
else if (lastTag.equals(XML.EL_COLUMN_NAME)) else if (lastTag.equals(XML.EL_COLUMN_NAME))
{ {
if (dboStack.peek() instanceof PrimaryKey) if (stack.peek() instanceof PrimaryKey)
{ {
PrimaryKey pk = (PrimaryKey) dboStack.peek(); PrimaryKey pk = (PrimaryKey) stack.peek();
pk.getColumnNames().add(lastText); pk.getColumnNames().add(lastText);
} }
else if (dboStack.peek() instanceof Index) else if (stack.peek() instanceof Index)
{ {
Index index = (Index) dboStack.peek(); Index index = (Index) stack.peek();
index.getColumnNames().add(lastText); index.getColumnNames().add(lastText);
} }
} }
else if (lastTag.equals(XML.EL_LOCAL_COLUMN)) else if (lastTag.equals(XML.EL_LOCAL_COLUMN))
{ {
ForeignKey fk = (ForeignKey) dboStack.peek(); ForeignKey fk = (ForeignKey) stack.peek();
fk.setLocalColumn(lastText); fk.setLocalColumn(lastText);
} }
else if (lastTag.equals(XML.EL_TARGET_TABLE)) else if (lastTag.equals(XML.EL_TARGET_TABLE))
{ {
ForeignKey fk = (ForeignKey) dboStack.peek(); ForeignKey fk = (ForeignKey) stack.peek();
fk.setTargetTable(lastText); fk.setTargetTable(lastText);
} }
else if (lastTag.equals(XML.EL_TARGET_COLUMN)) else if (lastTag.equals(XML.EL_TARGET_COLUMN))
{ {
ForeignKey fk = (ForeignKey) dboStack.peek(); ForeignKey fk = (ForeignKey) stack.peek();
fk.setTargetColumn(lastText); fk.setTargetColumn(lastText);
} }
else if (lastTag.equals(XML.EL_PROPERTY))
{
String propValue = lastText;
String propName = (String) stack.pop();
if (stack.peek() instanceof DbValidator)
{
@SuppressWarnings("unchecked")
DbValidator<? extends DbObject> validator = (DbValidator<? extends DbObject>) stack.peek();
validator.setProperty(propName, propValue);
}
}
} }
} }
} }

View File

@@ -28,9 +28,12 @@ import java.io.InputStream;
import java.util.Iterator; import java.util.Iterator;
import org.alfresco.util.schemacomp.model.DbObject; import org.alfresco.util.schemacomp.model.DbObject;
import org.alfresco.util.schemacomp.model.Index;
import org.alfresco.util.schemacomp.model.Schema; import org.alfresco.util.schemacomp.model.Schema;
import org.alfresco.util.schemacomp.model.Sequence; import org.alfresco.util.schemacomp.model.Sequence;
import org.alfresco.util.schemacomp.model.Table; import org.alfresco.util.schemacomp.model.Table;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.alfresco.util.schemacomp.validator.NameValidator;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -89,11 +92,17 @@ public class XMLToSchemaTest
assertEquals("nodeRef", table.getForeignKeys().get(0).getTargetColumn()); assertEquals("nodeRef", table.getForeignKeys().get(0).getTargetColumn());
assertEquals(1, table.getIndexes().size()); assertEquals(1, table.getIndexes().size());
assertEquals("idx_node_by_id", table.getIndexes().get(0).getName()); Index index = table.getIndexes().get(0);
assertEquals(true, table.getIndexes().get(0).isUnique()); assertEquals("idx_node_by_id", index.getName());
assertEquals(2, table.getIndexes().get(0).getColumnNames().size()); assertEquals(true, index.isUnique());
assertEquals("id", table.getIndexes().get(0).getColumnNames().get(0)); assertEquals(2, index.getColumnNames().size());
assertEquals("nodeRef", table.getIndexes().get(0).getColumnNames().get(1)); assertEquals("id", index.getColumnNames().get(0));
assertEquals("nodeRef", index.getColumnNames().get(1));
assertEquals(1, index.getValidators().size());
DbValidator<? extends DbObject> validator = index.getValidators().get(0);
assertEquals(NameValidator.class, validator.getClass());
assertEquals(1, validator.getPropertyNames().size());
assertEquals("idx_.+", validator.getProperty("pattern"));
assertEquals("node_seq", ((Sequence) objects.next()).getName()); assertEquals("node_seq", ((Sequence) objects.next()).getName());
assertEquals("person_seq", ((Sequence) objects.next()).getName()); assertEquals("person_seq", ((Sequence) objects.next()).getName());

View File

@@ -18,12 +18,17 @@
*/ */
package org.alfresco.util.schemacomp.model; 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.ComparisonUtils;
import org.alfresco.util.schemacomp.DbProperty; import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DefaultComparisonUtils; import org.alfresco.util.schemacomp.DefaultComparisonUtils;
import org.alfresco.util.schemacomp.DiffContext; import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Result.Strength; import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Results; import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.validator.DbValidator;
/** /**
* Useful base class for many, if not all the {@link DbObject} implementations. * Useful base class for many, if not all the {@link DbObject} implementations.
@@ -37,6 +42,7 @@ public abstract class AbstractDbObject implements DbObject
/** How differences in the name field should be reported */ /** How differences in the name field should be reported */
private Strength nameStrength = Strength.ERROR; private Strength nameStrength = Strength.ERROR;
protected ComparisonUtils comparisonUtils = new DefaultComparisonUtils(); protected ComparisonUtils comparisonUtils = new DefaultComparisonUtils();
private List<DbValidator<?>> validators = new ArrayList<DbValidator<?>>();
/** /**
@@ -203,4 +209,28 @@ public abstract class AbstractDbObject implements DbObject
{ {
this.comparisonUtils = comparisonUtils; this.comparisonUtils = comparisonUtils;
} }
@Override
public List<DbValidator<? extends DbObject>> getValidators()
{
return validators;
}
/**
* @param validators the validators to set
*/
@Override
public void setValidators(List<DbValidator<? extends DbObject>> validators)
{
if (validators == null)
{
this.validators = Collections.emptyList();
}
else
{
this.validators = validators;
}
}
} }

View File

@@ -24,6 +24,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor; import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty; import org.alfresco.util.schemacomp.DbProperty;
@@ -32,6 +34,8 @@ import org.alfresco.util.schemacomp.Difference.Where;
import org.alfresco.util.schemacomp.Result.Strength; import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.Results; import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.ValidationResult; import org.alfresco.util.schemacomp.ValidationResult;
import org.alfresco.util.schemacomp.validator.AbstractDbValidator;
import org.alfresco.util.schemacomp.validator.DbValidator;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -84,7 +88,6 @@ public class AbstractDbObjectTest
assertTrue("The very same object with non-null name", dbObject.sameAs(dbObject)); assertTrue("The very same object with non-null name", dbObject.sameAs(dbObject));
} }
@Test @Test
public void diff() public void diff()
{ {
@@ -110,6 +113,25 @@ public class AbstractDbObjectTest
} }
@SuppressWarnings("unchecked")
@Test
public void canGetValidators()
{
List<DbValidator<? extends DbObject>> validators = dbObject.getValidators();
assertEquals(0, validators.size());
dbObject.setValidators(null);
validators = dbObject.getValidators();
assertEquals(0, validators.size());
dbObject.setValidators(validatorList(new TestValidator1(), new TestValidator2()));
validators = dbObject.getValidators();
assertEquals(2, validators.size());
assertEquals(TestValidator1.class, validators.get(0).getClass());
assertEquals(TestValidator2.class, validators.get(1).getClass());
}
/** /**
* Concrete DbObject for testing the AbstractDbObject base class. * Concrete DbObject for testing the AbstractDbObject base class.
*/ */
@@ -142,4 +164,27 @@ public class AbstractDbObjectTest
return this.someProp; return this.someProp;
} }
} }
private List<DbValidator<? extends DbObject>> validatorList(DbValidator<? extends DbObject>... validators)
{
return Arrays.asList(validators);
}
private static class TestValidator extends AbstractDbValidator<DbObject>
{
@Override
public void validate(DbObject reference, DbObject target, DiffContext ctx)
{
}
}
private static class TestValidator1 extends TestValidator
{
}
private static class TestValidator2 extends TestValidator
{
}
} }

View File

@@ -21,7 +21,6 @@ package org.alfresco.util.schemacomp.model;
import org.alfresco.util.schemacomp.DbObjectVisitor; import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DbProperty; import org.alfresco.util.schemacomp.DbProperty;
import org.alfresco.util.schemacomp.DiffContext; import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength; import org.alfresco.util.schemacomp.Result.Strength;
/** /**
@@ -116,7 +115,6 @@ public class Column extends AbstractDbObject
@Override @Override
protected void doDiff(DbObject right, DiffContext ctx, Strength strength) protected void doDiff(DbObject right, DiffContext ctx, Strength strength)
{ {
Results differences = ctx.getDifferences();
DbProperty thisTypeProp = new DbProperty(this, "type"); DbProperty thisTypeProp = new DbProperty(this, "type");
DbProperty thisNullableProp = new DbProperty(this, "nullable"); DbProperty thisNullableProp = new DbProperty(this, "nullable");

View File

@@ -18,10 +18,14 @@
*/ */
package org.alfresco.util.schemacomp.model; package org.alfresco.util.schemacomp.model;
import java.util.Collection;
import java.util.List;
import org.alfresco.util.schemacomp.DbObjectVisitor; import org.alfresco.util.schemacomp.DbObjectVisitor;
import org.alfresco.util.schemacomp.DiffContext; import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.Results; import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.Result.Strength; import org.alfresco.util.schemacomp.Result.Strength;
import org.alfresco.util.schemacomp.validator.DbValidator;
/** /**
* All database objects to be modelled for schema comparisons must implement this interface, examples * All database objects to be modelled for schema comparisons must implement this interface, examples
@@ -86,4 +90,20 @@ public interface DbObject
* @param parent * @param parent
*/ */
void setParent(DbObject parent); void setParent(DbObject parent);
/**
* Retrieve the list of validators associated with this database object.
*
* @see DbValidator
* @return DbValidator List
*/
List<DbValidator<? extends DbObject>> getValidators();
/**
* Set/override the validators associated with this database object.
*
* @param validators
*/
void setValidators(List<DbValidator<? extends DbObject>> validators);
} }

View File

@@ -0,0 +1,54 @@
/*
* 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.validator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.util.schemacomp.model.DbObject;
/**
* Base class providing DbValidator support.
*
* @author Matt Ward
*/
public abstract class AbstractDbValidator<T extends DbObject> implements DbValidator<T>
{
private final Map<String, String> properties = new HashMap<String, String>();
@Override
public void setProperty(String name, String value)
{
properties.put(name, value);
}
@Override
public String getProperty(String name)
{
return properties.get(name);
}
@Override
public Set<String> getPropertyNames()
{
return properties.keySet();
}
}

View File

@@ -18,6 +18,8 @@
*/ */
package org.alfresco.util.schemacomp.validator; package org.alfresco.util.schemacomp.validator;
import java.util.Set;
import org.alfresco.util.schemacomp.DiffContext; import org.alfresco.util.schemacomp.DiffContext;
import org.alfresco.util.schemacomp.model.DbObject; import org.alfresco.util.schemacomp.model.DbObject;
@@ -27,7 +29,13 @@ import org.alfresco.util.schemacomp.model.DbObject;
* *
* @author Matt Ward * @author Matt Ward
*/ */
public interface DbValidator public interface DbValidator<T extends DbObject>
{ {
void validate(DbObject reference, DbObject target, DiffContext ctx); void validate(T reference, T target, DiffContext ctx);
void setProperty(String name, String value);
String getProperty(String name);
Set<String> getPropertyNames();
} }

View File

@@ -18,8 +18,8 @@
*/ */
package org.alfresco.util.schemacomp.validator; package org.alfresco.util.schemacomp.validator;
import java.util.HashMap; import java.util.Set;
import java.util.Map; import java.util.TreeSet;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.util.schemacomp.DbProperty; import org.alfresco.util.schemacomp.DbProperty;
@@ -36,7 +36,7 @@ import org.hibernate.dialect.Dialect;
* *
* @author Matt Ward * @author Matt Ward
*/ */
public class NameValidator implements DbValidator public class NameValidator implements DbValidator<DbObject>
{ {
private Pattern pattern; private Pattern pattern;
@@ -58,4 +58,41 @@ public class NameValidator implements DbValidator
{ {
this.pattern = pattern; this.pattern = pattern;
} }
public Pattern getPattern()
{
return this.pattern;
}
@Override
public void setProperty(String name, String value)
{
if (name.equals("pattern") && value != null)
{
Pattern pattern = Pattern.compile(value);
setPattern(pattern);
}
}
@Override
public String getProperty(String name)
{
if (name.equals("pattern") && pattern != null)
{
return pattern.toString();
}
return null;
}
@Override
public Set<String> getPropertyNames()
{
Set<String> props = new TreeSet<String>();
props.add("pattern");
return props;
}
} }

View File

@@ -22,7 +22,10 @@ package org.alfresco.util.schemacomp.validator;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.util.schemacomp.DiffContext; import org.alfresco.util.schemacomp.DiffContext;
@@ -80,6 +83,14 @@ public class NameValidatorTest
} }
@Test
public void canSetPatternUsingProperties()
{
validator.setProperty("pattern", "ORA_[A-Z_]+");
assertEquals("ORA_[A-Z_]+", validator.getPattern().toString());
}
private DbObject indexForName(String name) private DbObject indexForName(String name)
{ {
return new Index(null, name, new ArrayList<String>()); return new Index(null, name, new ArrayList<String>());

View File

@@ -26,7 +26,7 @@ import org.alfresco.util.schemacomp.model.DbObject;
* *
* @author Matt Ward * @author Matt Ward
*/ */
public class NullValidator implements DbValidator public class NullValidator extends AbstractDbValidator<DbObject>
{ {
@Override @Override
public void validate(DbObject reference, DbObject target, DiffContext ctx) public void validate(DbObject reference, DbObject target, DiffContext ctx)

View File

@@ -37,6 +37,13 @@
<columnname>id</columnname> <columnname>id</columnname>
<columnname>nodeRef</columnname> <columnname>nodeRef</columnname>
</columnnames> </columnnames>
<validators>
<validator class="org.alfresco.util.schemacomp.validator.NameValidator">
<properties>
<property name="pattern">idx_.+</property>
</properties>
</validator>
</validators>
</index> </index>
</indexes> </indexes>
</table> </table>