net.ontopia.topicmaps.db2tm.RelationMapping Maven / Gradle / Ivy
The newest version!
/*
* #!
* Ontopia DB2TM
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* !#
*/
package net.ontopia.topicmaps.db2tm;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.xml.DefaultXMLReaderFactory;
import net.ontopia.xml.PrettyPrinter;
import net.ontopia.xml.SAXTracker;
import net.ontopia.xml.ValidatingContentHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
/**
* INTERNAL: DB2TM relation mapping definition. Container for a set of
* relations, entities and fields. The mapping can be instatiated by
* calling the static read() methods, which will read its defintion
* from an XML file.
*/
public class RelationMapping extends SAXTracker {
// --- define a logging category.
private static final Logger log = LoggerFactory.getLogger(RelationMapping.class);
protected XMLReader reader;
protected String name;
protected String commitMode;
protected File baseDirectory;
protected final Map datasources;
protected final Map relations;
protected final Map iprefixes;
protected Relation currel;
protected Entity curent;
protected Field curfield;
protected ValueIF curvcol;
protected Changelog cursync;
protected ExpressionVirtualColumn curecol;
RelationMapping() {
this.datasources = new HashMap<>();
this.relations = new HashMap<>();
this.iprefixes = new HashMap<>();
// default commit mode, never commit
this.commitMode = null;
keepContentsOf.addAll(Arrays.asList(
"subject-locator", "subject-identifier", "item-identifier", "topic-name",
"occurrence", "param", "condition", "expression-column"
));
}
public void compile() {
for (Relation rel : getRelations()) {
rel.compile();
}
}
public void close() {
for (DataSourceIF ds : getDataSources()) {
try {
ds.close();
} catch (Throwable t) {
// ignore
}
}
}
protected static InputSource getRelaxNGSchema() throws IOException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream i = cl.getResourceAsStream("net/ontopia/topicmaps/db2tm/db2tm.rnc");
return new InputSource(i);
}
public File getBaseDirectory() {
return this.baseDirectory;
}
public void setBaseDirectory(File baseDirectory) {
this.baseDirectory = baseDirectory;
}
public String getName() {
return name;
}
public String getCommitMode() {
return commitMode;
}
public Collection getDataSources() {
return datasources.values();
}
public DataSourceIF getDataSource(String id) {
return datasources.get(id);
}
public void addDataSource(String id, DataSourceIF datasource) {
datasources.put(id, datasource);
}
public Collection getRelations() {
return relations.values();
}
public Relation getRelation(String name) {
return relations.get(name);
}
public void addRelation(Relation relation) {
String name = relation.getName();
if (relations.containsKey(name)) {
throw new DB2TMException("Duplicate relation: " + name);
} else {
relations.put(name, relation);
}
}
public Prefix getPrefix(String prefix) {
return iprefixes.get(prefix);
}
public String getQueryDeclarations() {
// create prefix declaration string
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Prefix prefix : iprefixes.values()) {
if (!first) { sb.append("\n"); }
first = false;
sb.append("using ");
sb.append(prefix.getId());
sb.append(" for ");
switch (prefix.getType()) {
case Prefix.TYPE_SUBJECT_IDENTIFIER:
sb.append("i\"");
break;
case Prefix.TYPE_ITEM_IDENTIFIER:
sb.append("s\"");
break;
case Prefix.TYPE_SUBJECT_LOCATOR:
sb.append("a\"");
break;
default:
break;
}
sb.append(prefix.getLocator());
sb.append("\"");
}
return sb.toString();
}
public static RelationMapping read(File file) throws IOException {
File parentFile = file.getParentFile();
return read(new FileInputStream(file), parentFile);
}
public static RelationMapping readFromClasspath(String resource) throws IOException {
if (resource == null) {
throw new DB2TMConfigException("Parameter 'resource' must be specified.");
}
ClassLoader cloader = RelationMapping.class.getClassLoader();
InputStream istream = cloader.getResourceAsStream(resource);
if (istream != null) {
log.debug("{}: loading from classpath", resource);
return read(istream, null);
} else {
throw new DB2TMConfigException("Resource '" + resource + "' not found on classpath.");
}
}
public static RelationMapping read(InputStream istream, File basedir)
throws IOException {
// Create new parser object
XMLReader parser;
try {
parser = new DefaultXMLReaderFactory().createXMLReader();
} catch (SAXException e) {
throw new IOException("Problems occurred when creating SAX2 XMLReader: " +
e.getMessage());
}
// Create content handler
RelationMapping mapping = new RelationMapping();
mapping.setBaseDirectory(basedir);
ContentHandler vhandler =
new ValidatingContentHandler(mapping, getRelaxNGSchema(), true);
parser.setContentHandler(vhandler);
try {
// Parse input source
parser.parse(new InputSource(istream));
} catch (FileNotFoundException e) {
log.error("Resource not found: {}", e.getMessage());
throw e;
} catch (SAXParseException e) {
throw new OntopiaRuntimeException("XML parsing problem: " + e.toString() + " at: "+
e.getSystemId() + ":" + e.getLineNumber() + ":" +
e.getColumnNumber(), e);
} catch (SAXException e) {
if (e.getException() instanceof IOException) {
throw (IOException) e.getException();
}
throw new OntopiaRuntimeException(e);
}
// Compile mapping
mapping.compile();
return mapping;
}
// --------------------------------------------------------------------------
// Content handler
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// Document events
// --------------------------------------------------------------------------
@Override
public void startElement(String nsuri, String lname, String qname,
Attributes attrs) throws SAXException {
// Relations
if ("relation".equals(lname)) {
currel = new Relation(this);
currel.setName(getValue(attrs, "name"));
currel.setColumns(getValues(attrs, "columns", "column"));
currel.setPrimaryKey(getValues(attrs, "primary-key"));
currel.setCommitMode(getValue(attrs, "commit-mode"));
String synctype = getValue(attrs, "synctype");
if (synctype == null) {
currel.setSynchronizationType(Relation.SYNCHRONIZATION_UNKNOWN);
} else if ("none".equals(synctype)) {
currel.setSynchronizationType(Relation.SYNCHRONIZATION_NONE);
} else if ("rescan".equals(synctype)) {
currel.setSynchronizationType(Relation.SYNCHRONIZATION_RESCAN);
} else if ("changelog".equals(synctype)) {
currel.setSynchronizationType(Relation.SYNCHRONIZATION_CHANGELOG);
}
addRelation(currel);
}
// Entities
else if ("topic".equals(lname)) {
curent = new Entity(Entity.TYPE_TOPIC, currel);
String primary = getValue(attrs, "primary");
if (primary != null) {
curent.setPrimary(Boolean.valueOf(primary));
}
curent.setId(getValue(attrs, "id"));
String condition = getValue(attrs, "condition");
if (condition != null) {
curent.setConditionValue(Values.getColumnValue(currel, condition));
}
curent.setTypes(getValues(attrs, "types", "type"));
currel.addEntity(curent);
}
else if ("association".equals(lname)) {
curent = new Entity(Entity.TYPE_ASSOCIATION, currel);
String primary = getValue(attrs, "primary");
if (primary != null) {
curent.setPrimary(Boolean.valueOf(primary));
}
curent.setId(getValue(attrs, "id"));
String condition = getValue(attrs, "condition");
if (condition != null) {
curent.setConditionValue(Values.getColumnValue(currel, condition));
}
curent.setAssociationType(getValue(attrs, "type"));
curent.setScope(getValues(attrs, "scope"));
currel.addEntity(curent);
}
// Identity Fields
else if ("subject-locator".equals(lname)) {
curfield = new Field(Field.TYPE_SUBJECT_LOCATOR, curent);
curfield.setColumn(getValue(attrs, "column"));
curent.addField(curfield);
}
else if ("subject-identifier".equals(lname)) {
curfield = new Field(Field.TYPE_SUBJECT_IDENTIFIER, curent);
curfield.setColumn(getValue(attrs, "column"));
curent.addField(curfield);
}
else if ("item-identifier".equals(lname)) {
curfield = new Field(Field.TYPE_ITEM_IDENTIFIER, curent);
curfield.setColumn(getValue(attrs, "column"));
curent.addField(curfield);
}
// Characteristics
else if ("occurrence".equals(lname)) {
curfield = new Field(Field.TYPE_OCCURRENCE, curent);
curfield.setColumn(getValue(attrs, "column"));
curfield.setType(getValue(attrs, "type"));
curfield.setScope(getValues(attrs, "scope"));
curfield.setDatatype(getValue(attrs, "datatype"));
curent.addField(curfield);
}
else if ("topic-name".equals(lname)) {
curfield = new Field(Field.TYPE_TOPIC_NAME, curent);
curfield.setColumn(getValue(attrs, "column"));
curfield.setType(getValue(attrs, "type"));
curfield.setScope(getValues(attrs, "scope"));
curent.addField(curfield);
}
else if ("player".equals(lname)) {
curfield = new Field(Field.TYPE_PLAYER, curent);
curfield.setRoleType(getValue(attrs, "rtype"));
curfield.setAssociationType(getValue(attrs, "atype"));
curfield.setScope(getValues(attrs, "scope"));
curent.addField(curfield);
}
else if ("other".equals(lname)) {
Field orole = new Field(Field.TYPE_ASSOCIATION_ROLE, curent);
orole.setRoleType(getValue(attrs, "rtype"));
orole.setPlayer(getValue(attrs, "player"));
String optional = getValue(attrs, "optional");
if (optional != null) {
orole.setOptional(Boolean.parseBoolean(optional));
}
curfield.addOtherRoleField(orole);
}
else if ("role".equals(lname)) {
curfield = new Field(Field.TYPE_ASSOCIATION_ROLE, curent);
curfield.setColumn(getValue(attrs, "column"));
curfield.setRoleType(getValue(attrs, "type"));
curfield.setPlayer(getValue(attrs, "player"));
String optional = getValue(attrs, "optional");
if (optional != null) {
curfield.setOptional(Boolean.valueOf(optional).booleanValue());
}
curent.addField(curfield);
}
// Virtual columns
else if ("mapping-column".equals(lname)) {
String colname = getValue(attrs, "name");
String inputName = getValue(attrs, "column");
curvcol = new MappingVirtualColumn(currel, colname, inputName);
currel.addVirtualColumn(colname, curvcol);
}
else if ("map".equals(lname)) {
((MappingVirtualColumn)curvcol).addMapping(getValue(attrs, "from"), getValue(attrs, "to"));
}
else if ("default".equals(lname)) {
((MappingVirtualColumn)curvcol).setDefault(getValue(attrs, "to"));
}
// Function columns
else if ("function-column".equals(lname)) {
String colname = getValue(attrs, "name");
String method = getValue(attrs, "method");
curvcol = new FunctionVirtualColumn(currel, colname, method);
currel.addVirtualColumn(colname, curvcol);
}
// Sync
else if ("changelog".equals(lname)) {
cursync = new Changelog(currel);
cursync.setTable(getValue(attrs, "table"));
cursync.setPrimaryKey(getValues(attrs, "primary-key"));
cursync.setOrderColumn(getValue(attrs, "order-column"));
cursync.setLocalOrderColumn(getValue(attrs, "local-order-column"));
cursync.setCondition(getValue(attrs, "condition"));
currel.addSync(cursync);
if (currel.getSynchronizationType() == Relation.SYNCHRONIZATION_UNKNOWN) {
currel.setSynchronizationType(Relation.SYNCHRONIZATION_CHANGELOG);
}
}
else if ("extent".equals(lname)) {
curent.addExtentQuery(getValue(attrs, "query"));
}
else if ("expression-column".equals(lname)) {
curecol = new ExpressionVirtualColumn(getValue(attrs, "name"));
cursync.addVirtualColumn(curecol);
}
// Prefixes
else if ("using".equals(lname)) {
String prefix = getValue(attrs, "prefix");
int type = Prefix.TYPE_SUBJECT_IDENTIFIER;
String locator = getValue(attrs, "subject-identifier");
if (locator != null) {
type = Prefix.TYPE_SUBJECT_IDENTIFIER;
}
if (locator == null) {
locator = getValue(attrs, "item-identifier");
if (locator != null) {
type = Prefix.TYPE_ITEM_IDENTIFIER;
}
}
if (locator == null) {
locator = getValue(attrs, "subject-locator");
if (locator != null) {
type = Prefix.TYPE_SUBJECT_LOCATOR;
}
}
iprefixes.put(prefix, new Prefix(prefix, locator, type));
}
// Other
else if ("db2tm".equals(lname)) {
name = getValue(attrs, "name");
commitMode = getValue(attrs, "commit-mode");
}
// Sources
else if ("sources".equals(lname)) {
}
else if ("csv".equals(lname)) {
String id = getValue(attrs, "id");
CSVDataSource datasource = new CSVDataSource(this);
// - path
datasource.setPath(getValue(attrs, "path"));
// - encoding
String encoding = getValue(attrs, "encoding");
if (encoding != null) {
datasource.setEncoding(getValue(attrs, "encoding"));
}
// - separator
String separator = getValue(attrs, "separator");
if (separator != null) {
datasource.setSeparator(separator.charAt(0));
}
// - quoting
String quoting = getValue(attrs, "quoting");
if (quoting != null) {
datasource.setQuoteCharacter(quoting.charAt(0));
}
// - ignoreFirstLines
String ignoreFirstLines = getValue(attrs, "ignoreFirstLines");
if (ignoreFirstLines != null) {
datasource.setIgnoreFirstLines(Integer.parseInt(ignoreFirstLines));
}
datasources.put(id, datasource);
}
else if ("jdbc".equals(lname)) {
String id = getValue(attrs, "id");
JDBCDataSource datasource = new JDBCDataSource(this);
datasource.setPropertyFile(getValue(attrs, "propfile"));
datasources.put(id, datasource);
}
// call super
super.startElement(nsuri, lname, qname, attrs);
}
@Override
public void endElement(String nsuri, String lname, String qname)
throws SAXException {
// call super
super.endElement(nsuri, lname, qname);
if (null != lname) {
switch (lname) {
case "subject-locator":
case "subject-identifier":
case "item-identifier":
case "topic-name":
case "occurrence":
curfield.setPattern(content.toString());
break;
case "relation":
currel = null;
break;
case "topic":
case "association":
curent = null;
break;
case "param":
((FunctionVirtualColumn)curvcol).addParameter(content.toString());
break;
case "condition":
currel.setCondition(content.toString());
break;
case "mapping-column":
curvcol = null;
break;
case "function-column":
((FunctionVirtualColumn)curvcol).compile();
curvcol = null;
break;
case "changelog":
cursync = null;
break;
case "expression-column":
curecol.setSQLExpression(content.toString());
curecol = null;
break;
default:
break;
}
}
}
// --------------------------------------------------------------------------
// Helpers
// --------------------------------------------------------------------------
protected String getValue(Attributes attrs, String name) {
return attrs.getValue("", name);
}
protected String getValue(Attributes attrs, String name, String _default) {
String result = attrs.getValue("", name);
return (result == null ? _default : result);
}
protected String[] getValues(Attributes attrs, String name) {
String value = getValue(attrs, name);
return (value == null)
? new String[] { }
: StringUtils.split(value, " \t\n\r,");
}
protected String[] getValues(Attributes attrs, String plural, String singular) {
String value = getValue(attrs, singular);
return (value != null)
? new String[] { value }
: getValues(attrs, plural);
}
protected void addAttribute(AttributesImpl atts, String name, String type, String value) {
if (value != null) {
atts.addAttribute("", "", name, type, value);
}
}
protected void addAttribute(AttributesImpl atts, String name, String type, String[] values) {
if (values != null) {
atts.addAttribute("", "", name, type, StringUtils.join(values, ","));
}
}
// --------------------------------------------------------------------------
// Export
// --------------------------------------------------------------------------
public void write(Writer writer) throws SAXException {
write(writer, "utf-8");
}
public void write(Writer writer, String encoding) throws SAXException {
write(new PrettyPrinter(writer, encoding));
}
protected void write(ContentHandler dh) throws SAXException {
// initialize attributes
AttributesImpl atts = new AttributesImpl();
//
if (name != null) {
addAttribute(atts, "name", "CDATA", name);
}
dh.startDocument();
dh.startElement("", "", "db2tm", atts);
atts.clear();
// prefixes
for (Prefix prefix : iprefixes.values()) {
addAttribute(atts, "prefix", "CDATA", prefix.getId());
switch (prefix.getType()) {
case Prefix.TYPE_SUBJECT_IDENTIFIER:
addAttribute(atts, "subject-identifier", "CDATA", prefix.getLocator());
break;
case Prefix.TYPE_ITEM_IDENTIFIER:
addAttribute(atts, "item-identifier", "CDATA", prefix.getLocator());
break;
case Prefix.TYPE_SUBJECT_LOCATOR:
addAttribute(atts, "subject-locator", "CDATA", prefix.getLocator());
break;
}
dh.startElement("", "", "using", atts);
atts.clear();
dh.endElement("", "", "using");
}
// relations
for (Relation rel : getRelations()) {
//
addAttribute(atts, "name", "CDATA", rel.getName());
addAttribute(atts, "columns", "CDATA", rel.getColumns());
dh.startElement("", "", "relation", atts);
atts.clear();
outputEntities(rel, dh);
//
dh.endElement("", "", "relation");
}
//
dh.endElement("", "", "db2tm");
dh.endDocument();
}
protected void outputEntities(Relation rel, ContentHandler dh) throws SAXException {
AttributesImpl atts = new AttributesImpl();
for (Entity entity : rel.getEntities()) {
if (entity.getEntityType() == Entity.TYPE_TOPIC) {
//
if (entity.getId() != null) {
addAttribute(atts, "id", "CDATA", entity.getId());
}
addAttribute(atts, "type", "CDATA", entity.getAssociationType());
dh.startElement("", "", "topic", atts);
atts.clear();
outputFields(entity, dh);
//
dh.endElement("", "", "topic");
} else if (entity.getEntityType() == Entity.TYPE_ASSOCIATION) {
//
if (entity.getId() != null) {
addAttribute(atts, "id", "CDATA", entity.getId());
}
addAttribute(atts, "type", "CDATA", entity.getAssociationType());
addAttribute(atts, "scope", "CDATA", entity.getScope());
dh.startElement("", "", "association", atts);
atts.clear();
outputFields(entity, dh);
//
dh.endElement("", "", "association");
}
}
}
protected void outputFields(Entity entity, ContentHandler dh) throws SAXException {
for (Field field : entity.getIdentityFields()) {
outputField(field, dh);
}
for (Field field : entity.getRoleFields()) {
outputField(field, dh);
}
for (Field field : entity.getCharacteristicFields()) {
outputField(field, dh);
}
}
protected void outputField(Field field, ContentHandler dh) throws SAXException {
AttributesImpl atts = new AttributesImpl();
if (field.getFieldType() == Field.TYPE_SUBJECT_LOCATOR) {
addAttribute(atts, "column", "CDATA", field.getColumn());
dh.startElement("", "", "subject-locator", atts);
char[] c = field.getPattern().toCharArray();
dh.characters(c, 0, c.length);
dh.endElement("", "", "subject-locator");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_SUBJECT_IDENTIFIER) {
addAttribute(atts, "column", "CDATA", field.getColumn());
dh.startElement("", "", "subject-identifier", atts);
char[] c = field.getPattern().toCharArray();
dh.characters(c, 0, c.length);
dh.endElement("", "", "subject-identifier");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_ITEM_IDENTIFIER) {
addAttribute(atts, "column", "CDATA", field.getColumn());
dh.startElement("", "", "item-identifier", atts);
char[] c = field.getPattern().toCharArray();
dh.characters(c, 0, c.length);
dh.endElement("", "", "item-identifier");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_OCCURRENCE) {
addAttribute(atts, "column", "CDATA", field.getColumn());
addAttribute(atts, "type", "CDATA", field.getType());
addAttribute(atts, "scope", "CDATA", field.getScope());
addAttribute(atts, "datatype", "CDATA", field.getDatatype());
dh.startElement("", "", "occurrence", atts);
dh.endElement("", "", "occurrence");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_TOPIC_NAME) {
addAttribute(atts, "column", "CDATA", field.getColumn());
addAttribute(atts, "type", "CDATA", field.getType());
addAttribute(atts, "scope", "CDATA", field.getScope());
dh.startElement("", "", "topic-name", atts);
dh.endElement("", "", "topic-name");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_PLAYER) {
addAttribute(atts, "rtype", "CDATA", field.getRoleType());
addAttribute(atts, "atype", "CDATA", field.getAssociationType());
addAttribute(atts, "scope", "CDATA", field.getScope());
dh.startElement("", "", "player", atts);
atts.clear();
for (Field orole : field.getOtherRoleFields()) {
addAttribute(atts, "rtype", "CDATA", orole.getRoleType());
addAttribute(atts, "player", "CDATA", orole.getPlayer());
dh.startElement("", "", "other", atts);
dh.endElement("", "", "other");
atts.clear();
}
dh.endElement("", "", "player");
atts.clear();
}
else if (field.getFieldType() == Field.TYPE_ASSOCIATION_ROLE) {
addAttribute(atts, "type", "CDATA", field.getRoleType());
addAttribute(atts, "player", "CDATA", field.getPlayer());
dh.startElement("", "", "role", atts);
dh.endElement("", "", "role");
atts.clear();
}
else {
throw new OntopiaRuntimeException("Unknown field type: " + field.getType());
}
}
@Override
public String toString() {
return "RelationMapping(" + getName() + ")";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy