/* * #! * Ontopia OSL Schema * #- * Copyright (C) 2001 - 2014 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.schema.impl.osl; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.util.Collection; import java.util.Iterator; import net.ontopia.infoset.core.LocatorIF; import net.ontopia.topicmaps.schema.core.CardinalityConstraintIF; import net.ontopia.topicmaps.schema.core.SchemaIF; import net.ontopia.topicmaps.schema.core.SchemaWriterIF; import net.ontopia.topicmaps.schema.core.TMObjectMatcherIF; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.xml.PrettyPrinter; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * PUBLIC: Writes out an OSL schema using the OSL schema syntax. */ public class OSLSchemaWriter implements SchemaWriterIF { protected Writer out; protected String encoding; protected LocatorIF base; protected AttributesImpl EMPTY_ATTR_LIST; /** * PUBLIC: Creates a schema writer bound to the given Writer object. * @param encoding The encoding in which the schema will be written. */ public OSLSchemaWriter(Writer out, String encoding) { this.out = out; this.encoding = encoding; EMPTY_ATTR_LIST = new AttributesImpl(); } /** * PUBLIC: Creates a schema writer bound to the given file. * @param encoding The encoding in which to write the file. */ public OSLSchemaWriter(File file, String encoding) throws java.io.IOException { this.out = new OutputStreamWriter(new FileOutputStream(file), encoding); this.encoding = encoding; EMPTY_ATTR_LIST = new AttributesImpl(); } // --- SchemaWriterIF methods /** * PUBLIC: Writes the schema. */ public void write(SchemaIF schema) throws java.io.IOException { PrintWriter print = new PrintWriter(out); try { base = schema.getAddress(); export((OSLSchema) schema, new PrettyPrinter(print, encoding)); } catch (SAXException e) { throw new net.ontopia.utils.OntopiaRuntimeException(e); } print.flush(); } // --- Export methods protected void export(OSLSchema schema, ContentHandler dh) throws SAXException { dh.startElement("", "", "tm-schema", getAttributes(schema.isStrict(), "match", "strict", "loose")); Iterator it = schema.getRuleSets().iterator(); while (it.hasNext()) export((RuleSet) it.next(), dh); it = schema.getTopicClasses().iterator(); while (it.hasNext()) export((TopicClass) it.next(), dh); it = schema.getAssociationClasses().iterator(); while (it.hasNext()) export((AssociationClass) it.next(), dh); dh.endElement("", "", "tm-schema"); } protected void export(RuleSet ruleset, ContentHandler dh) throws SAXException { dh.startElement("", "", "ruleset", getAttributes("id", ruleset.getId())); export((TopicConstraintCollection) ruleset, dh); dh.endElement("", "", "ruleset"); } protected void export(TopicClass klass, ContentHandler dh) throws SAXException { AttributesImpl atts = (AttributesImpl) getAttributes(klass.isStrict(), "match", "strict", "loose"); if (klass.getId() != null) atts.addAttribute("", "", "id", "CDATA", klass.getId()); dh.startElement("", "", "topic", atts); exportInstanceOf(klass.getTypeSpecification(), dh); // otherClass Iterator it = klass.getOtherClasses().iterator(); while (it.hasNext()) { TypeSpecification typespec = (TypeSpecification) it.next(); dh.startElement("", "", "otherClass", EMPTY_ATTR_LIST); export(typespec.getClassMatcher(), dh); dh.endElement("", "", "otherClass"); } // superclass if (klass.getSuperclass() != null) { TopicClass superclass = klass.getSuperclass(); // FIXME: what if no id? dh.startElement("", "", "superclass", getAttributes("ref", superclass.getId())); dh.endElement("", "", "superclass"); } export((TopicConstraintCollection) klass, dh); dh.endElement("", "", "topic"); } protected void export(AssociationClass klass, ContentHandler dh) throws SAXException { dh.startElement("", "", "association", EMPTY_ATTR_LIST); exportInstanceOf(klass.getTypeSpecification(), dh); Iterator it = klass.getRoleConstraints().iterator(); while (it.hasNext()) export((AssociationRoleConstraint) it.next(), dh); dh.endElement("", "", "association"); } protected void export(TopicConstraintCollection constraint, ContentHandler dh) throws SAXException { Iterator it = constraint.getSubRules().iterator(); while (it.hasNext()) { RuleSet subrule = (RuleSet) it.next(); emptyElement(dh, "ruleref", getAttributes("rule", subrule.getId())); } it = constraint.getTopicNameConstraints().iterator(); while (it.hasNext()) export((TopicNameConstraint) it.next(), dh); it = constraint.getOccurrenceConstraints().iterator(); while (it.hasNext()) export((OccurrenceConstraint) it.next(), dh); it = constraint.getRoleConstraints().iterator(); while (it.hasNext()) export((TopicRoleConstraint) it.next(), dh); } protected void export(TopicNameConstraint constraint, ContentHandler dh) throws SAXException { dh.startElement("", "", "baseName", getMinMax(constraint)); exportScope(constraint, dh); Iterator it = constraint.getVariantConstraints().iterator(); while (it.hasNext()) export((VariantConstraint) it.next(), dh); dh.endElement("", "", "baseName"); } protected void export(VariantConstraint constraint, ContentHandler dh) throws SAXException { dh.startElement("", "", "variant", getMinMax(constraint)); exportScope(constraint, dh); dh.endElement("", "", "variant"); } protected void export(OccurrenceConstraint constraint, ContentHandler dh) throws SAXException { AttributesImpl atts = (AttributesImpl) getMinMax(constraint); String value = "either"; if (constraint.getInternal() == OccurrenceConstraint.RESOURCE_INTERNAL) value = "yes"; else if (constraint.getInternal() == OccurrenceConstraint.RESOURCE_EXTERNAL) value = "no"; atts.addAttribute("", "", "internal", "CDATA", value); dh.startElement("", "", "occurrence", atts); exportInstanceOf(constraint.getTypeSpecification(), dh); exportScope(constraint, dh); dh.endElement("", "", "occurrence"); } protected void export(TopicRoleConstraint constraint, ContentHandler dh) throws SAXException { dh.startElement("", "", "playing", getMinMax(constraint)); exportInstanceOf(constraint.getTypeSpecification(), dh); Iterator it = constraint.getAssociationTypes().iterator(); if (it.hasNext()) { dh.startElement("", "", "in", EMPTY_ATTR_LIST); while (it.hasNext()) { TypeSpecification spec = (TypeSpecification) it.next(); exportInstanceOf(spec, dh); } dh.endElement("", "", "in"); } dh.endElement("", "", "playing"); } protected void export(AssociationRoleConstraint constraint, ContentHandler dh) throws SAXException { dh.startElement("", "", "role", getMinMax(constraint)); exportInstanceOf(constraint.getTypeSpecification(), dh); Iterator it = constraint.getPlayerTypes().iterator(); while (it.hasNext()) { TypeSpecification spec = (TypeSpecification) it.next(); dh.startElement("", "", "player", getAttributes(spec.getSubclasses(), "subclasses", "yes", "no")); export(spec.getClassMatcher(), dh); dh.endElement("", "", "player"); } dh.endElement("", "", "role"); } protected void export(TMObjectMatcherIF matcher, ContentHandler dh) throws SAXException { if (matcher == null) return; else if (matcher instanceof InternalTopicRefMatcher) emptyElement(dh, "internalTopicRef", getAttributes("href", ((InternalTopicRefMatcher) matcher).getRelativeURI())); else if (matcher instanceof SourceLocatorMatcher) emptyElement(dh, "topicRef", getAttributes("href", getRelativeLocator(base, ((SourceLocatorMatcher) matcher).getLocator()))); else if (matcher instanceof AnyTopicMatcher) emptyElement(dh, "any", EMPTY_ATTR_LIST); else if (matcher instanceof SubjectIndicatorMatcher) emptyElement(dh, "subjectIndicatorRef", getAttributes("href", getRelativeLocator(base, ((SubjectIndicatorMatcher) matcher).getLocator()))); else if (matcher instanceof TypeSpecification) exportInstanceOf((TypeSpecification) matcher, dh); else throw new OntopiaRuntimeException("INTERNAL: Unknown matcher " + matcher); } protected void exportScope(ScopedConstraintIF constraint, ContentHandler dh) throws SAXException { if (constraint.getScopeSpecification() == null) return; int match = constraint.getScopeSpecification().getMatch(); if (match == ScopeSpecification.MATCH_SUPERSET) dh.startElement("", "", "scope", getAttributes("match", "superset")); else if (match == ScopeSpecification.MATCH_SUBSET) dh.startElement("", "", "scope", getAttributes("match", "subset")); else dh.startElement("", "", "scope", EMPTY_ATTR_LIST); exportMatchers(constraint.getScopeSpecification().getThemeMatchers(), dh); dh.endElement("", "", "scope"); } protected void exportInstanceOf(TypeSpecification spec, ContentHandler dh) throws SAXException { dh.startElement("", "", "instanceOf", getAttributes(spec.getSubclasses(), "subclasses", "yes", "no")); export(spec.getClassMatcher(), dh); dh.endElement("", "", "instanceOf"); } protected void exportMatchers(Collection matchers, ContentHandler dh) throws SAXException { Iterator it = matchers.iterator(); while (it.hasNext()) export((TMObjectMatcherIF) it.next(), dh); } // --- Internal helpers protected Attributes getAttributes(String name, String value) { AttributesImpl atts = new AttributesImpl(); if (value != null) atts.addAttribute("", "", name, "CDATA", value); return atts; } protected Attributes getAttributes(boolean setting, String name, String tvalue, String fvalue) { AttributesImpl atts = new AttributesImpl(); if (setting) atts.addAttribute("", "", name, "CDATA", tvalue); else atts.addAttribute("", "", name, "CDATA", fvalue); return atts; } protected Attributes getMinMax(CardinalityConstraintIF constraint) { AttributesImpl atts = new AttributesImpl(); if (constraint.getMinimum() != 0) atts.addAttribute("", "", "min", "CDATA", Integer.toString(constraint.getMinimum())); if (constraint.getMaximum() != CardinalityConstraintIF.INFINITY) atts.addAttribute("", "", "max", "CDATA", Integer.toString(constraint.getMaximum())); return atts; } protected void emptyElement(ContentHandler dh, String elem, Attributes atts) throws SAXException { dh.startElement("", "", elem, atts); dh.endElement("", "", elem); } protected String getRelativeLocator(LocatorIF base, LocatorIF relative) { if (base == null || !base.getNotation().equals(relative.getNotation())) return relative.getAddress(); String basea = base.getAddress(); String relativea = relative.getAddress(); if (relativea.startsWith(basea)) return relativea.substring(basea.length()); else return relativea; } }