/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available 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. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.io.gml.writer.internal.geometry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import eu.esdihumboldt.hale.common.schema.model.ChildDefinition; import eu.esdihumboldt.hale.common.schema.model.GroupPropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.PropertyDefinition; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.common.schema.model.constraint.property.Cardinality; import eu.esdihumboldt.hale.io.gml.writer.internal.StreamGmlWriter; /** * Represents a path in a type definition hierarchy (regarding subtypes and * properties) * * @author Simon Templer * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public class DefinitionPath { /** * Downcast path element */ private static class DowncastElement implements PathElement { private final QName elementName; private final TypeDefinition type; private final boolean unique; /** * Constructor * * @param elementName the name of the element the downcast is applied to * @param type the definition of the type that is downcast to * @param unique if the represented element cannot be repeated */ public DowncastElement(QName elementName, TypeDefinition type, boolean unique) { super(); this.elementName = elementName; this.type = type; this.unique = unique; } /** * @see PathElement#getName() */ @Override public QName getName() { return elementName; } /** * @see PathElement#getType() */ @Override public TypeDefinition getType() { return type; } /** * @see PathElement#isProperty() */ @Override public boolean isProperty() { return false; } /** * @see PathElement#isDowncast() */ @Override public boolean isDowncast() { return true; } /** * @see PathElement#prepareWrite(XMLStreamWriter) */ @Override public void prepareWrite(XMLStreamWriter writer) throws XMLStreamException { // add xsi:type writer.writeAttribute(StreamGmlWriter.SCHEMA_INSTANCE_NS, "type", getType().getName() .getLocalPart()); // XXX namespace needed for // the attribute value? } /** * @see PathElement#isUnique() */ @Override public boolean isUnique() { return unique; } /** * @see PathElement#isTransient() */ @Override public boolean isTransient() { return false; } /** * @see Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((elementName == null) ? 0 : elementName.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } /** * @see Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; DowncastElement other = (DowncastElement) obj; if (elementName == null) { if (other.elementName != null) return false; } else if (!elementName.equals(other.elementName)) return false; if (type == null) { if (other.type != null) return false; } else if (!type.equals(other.type)) return false; return true; } } // /** // * Sub-type path element // */ // private static class SubstitutionElement implements PathElement { // // private final SchemaElement element; // // private final boolean unique; // // /** // * Constructor // * // * @param element the substitution element // * @param unique if the represented element cannot be repeated // */ // public SubstitutionElement(SchemaElement element, boolean unique) { // this.element = element; // this.unique = unique; // } // // /** // * @see PathElement#getName() // */ // @Override // public Name getName() { // return element.getElementName(); // } // // /** // * @see PathElement#getType() // */ // @Override // public TypeDefinition getType() { // return element.getType(); // } // // /** // * @see PathElement#isProperty() // */ // @Override // public boolean isProperty() { // return false; // } // // /** // * @see PathElement#isDowncast() // */ // @Override // public boolean isDowncast() { // return false; // } // // /** // * @see PathElement#isUnique() // */ // @Override // public boolean isUnique() { // return unique; // } // // /** // * @see Object#hashCode() // */ // @Override // public int hashCode() { // final int prime = 31; // int result = 1; // result = prime * result // + ((element == null) ? 0 : element.hashCode()); // return result; // } // // /** // * @see Object#equals(Object) // */ // @Override // public boolean equals(Object obj) { // if (this == obj) // return true; // if (obj == null) // return false; // if (getClass() != obj.getClass()) // return false; // SubstitutionElement other = (SubstitutionElement) obj; // if (element == null) { // if (other.element != null) // return false; // } else if (!element.equals(other.element)) // return false; // return true; // } // // } /** * Path element representing a group. XXX a {@link DefinitionPath} that is * used for writing should never end on a {@link GroupElement}! * * @author Simon Templer */ private static class GroupElement implements PathElement { private final GroupPropertyDefinition groupDef; /** * Create a path element representing a group. * * @param groupDef the group property definition */ public GroupElement(GroupPropertyDefinition groupDef) { super(); this.groupDef = groupDef; } /** * @see PathElement#getName() */ @Override public QName getName() { return groupDef.getName(); } /** * @see PathElement#getType() */ @Override public TypeDefinition getType() { // no type for a group return null; } /** * @see PathElement#isProperty() */ @Override public boolean isProperty() { return false; } /** * @see PathElement#isDowncast() */ @Override public boolean isDowncast() { return false; } /** * @see PathElement#isUnique() */ @Override public boolean isUnique() { long max = groupDef.getConstraint(Cardinality.class).getMaxOccurs(); return max != Cardinality.UNBOUNDED && max <= 1; } /** * @see PathElement#isTransient() */ @Override public boolean isTransient() { return true; } @Override public void prepareWrite(XMLStreamWriter writer) throws XMLStreamException { // do nothing } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((groupDef == null) ? 0 : groupDef.hashCode()); return result; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; GroupElement other = (GroupElement) obj; if (groupDef == null) { if (other.groupDef != null) return false; } else if (!groupDef.equals(other.groupDef)) return false; return true; } } /** * A property path element */ private static class PropertyElement implements PathElement { private final PropertyDefinition propDef; /** * Constructor * * @param attdef the attribute definition */ public PropertyElement(PropertyDefinition attdef) { this.propDef = attdef; } /** * @see PathElement#getName() */ @Override public QName getName() { return propDef.getName(); } /** * @see PathElement#getType() */ @Override public TypeDefinition getType() { return propDef.getPropertyType(); } /** * @see PathElement#isProperty() */ @Override public boolean isProperty() { return true; } /** * @see PathElement#isDowncast() */ @Override public boolean isDowncast() { return false; } /** * @see PathElement#isUnique() */ @Override public boolean isUnique() { long max = propDef.getConstraint(Cardinality.class).getMaxOccurs(); return max != Cardinality.UNBOUNDED && max <= 1; } /** * @see PathElement#isTransient() */ @Override public boolean isTransient() { return false; } @Override public void prepareWrite(XMLStreamWriter writer) throws XMLStreamException { // do nothing } /** * @see Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((propDef == null) ? 0 : propDef.hashCode()); return result; } /** * @see Object#equals(Object) */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PropertyElement other = (PropertyElement) obj; if (propDef == null) { if (other.propDef != null) return false; } else if (!propDef.equals(other.propDef)) return false; return true; } } private final List<PathElement> steps = new ArrayList<PathElement>(); private TypeDefinition lastType; private QName lastName; private GeometryWriter<?> geometryWriter; private boolean lastUnique; private TypeDefinition geometryCompatibleType; /** * Create a definition path beginning with the given base path * * @param basePath the base path */ public DefinitionPath(DefinitionPath basePath) { this(basePath.lastType, basePath.lastName, basePath.lastUnique); steps.addAll(basePath.getSteps()); } /** * Create an empty definition path * * @param firstType the type starting the path * @param elementName the corresponding element name * @param unique if the element starting the path cannot be repeated */ public DefinitionPath(TypeDefinition firstType, QName elementName, boolean unique) { super(); lastType = firstType; lastName = elementName; lastUnique = unique; } /** * Create a path with at least one element. * * @param elements the path elements */ public DefinitionPath(List<PathElement> elements) { this(elements.get(elements.size() - 1).getType(), elements.get(elements.size() - 1) .getName(), elements.get(elements.size() - 1).isUnique()); steps.addAll(elements); } // /** // * Add a substitution // * // * @param element the substitution element // * // * @return this path for chaining // */ // public DefinitionPath addSubstitution(SchemaElement element) { // // 1. sub-type must override previous sub-type // // 2. sub-type must override a previous property XXX check this!!! or only the first? // // XXX -> therefore removing the previous path element // boolean unique = isLastUnique(); // // if (steps.size() > 0) { // steps.remove(steps.size() - 1); // } // // addStep(new SubstitutionElement(element, unique)); // // return this; // } /** * Add a downcast * * @param subtype the definition of the sub-type that is to be cast to * @return this path for chaining */ public DefinitionPath addDowncast(TypeDefinition subtype) { // 1. sub-type must override previous sub-type // 2. sub-type must override a previous property XXX check this!!! or // only the first? // XXX -> therefore removing the previous path element QName elementName = getLastName(); boolean unique = isLastUnique(); if (steps.size() > 0) { steps.remove(steps.size() - 1); } addStep(new DowncastElement(elementName, subtype, unique)); return this; } /** * Add a group to the path * * @param groupDef the group definition * @return this path for chaining */ public DefinitionPath addGroup(GroupPropertyDefinition groupDef) { steps.add(new GroupElement(groupDef)); return this; } /** * Add a child to the path * * @param child the child, either a group or property * @return this path for chaining */ public DefinitionPath add(ChildDefinition<?> child) { if (child.asGroup() != null) { return addGroup(child.asGroup()); } if (child.asProperty() != null) { return addProperty(child.asProperty()); } throw new IllegalArgumentException("Supplied an invalif child definition."); } private void addStep(PathElement step) { steps.add(step); lastType = step.getType(); lastName = step.getName(); lastUnique = step.isUnique(); } /** * Add a property * * @param property the property definition * * @return this path for chaining */ public DefinitionPath addProperty(PropertyDefinition property) { addStep(new PropertyElement(property)); return this; } /** * @return the geometryWriter */ public GeometryWriter<?> getGeometryWriter() { return geometryWriter; } /** * @return the geometryCompatibleType */ public TypeDefinition getGeometryCompatibleType() { return geometryCompatibleType; } /** * @param geometryWriter the geometryWriter to set * @param geometryCompatibleType the type that was identified as compatible * type by the writer */ public void setGeometryWriter(GeometryWriter<?> geometryWriter, TypeDefinition geometryCompatibleType) { this.geometryWriter = geometryWriter; this.geometryCompatibleType = geometryCompatibleType; } /** * @return the steps */ public List<PathElement> getSteps() { return Collections.unmodifiableList(steps); } /** * Determines if the path is empty * * @return if the path is empty */ public boolean isEmpty() { return steps.isEmpty(); } /** * Get the last type of the path. For empty paths this will be the type * specified on creation XXX not for groups * * @return the last type */ public TypeDefinition getLastType() { return lastType; } /** * Get the last name of the path. For empty paths this will be the name * specified on creation XXX not if last is a group * * @return the last type */ public QName getLastName() { return lastName; } /** * Get if the last element in the path is unique, which means that it cannot * be repeated XXX not if last is a group * * @return if the last element in the path is unique, which means that it * cannot be repeated */ public boolean isLastUnique() { return lastUnique; } /** * @see Object#toString() */ @Override public String toString() { StringBuffer result = null; for (PathElement step : steps) { if (result == null) { result = new StringBuffer(); } else { result.append(", "); //$NON-NLS-1$ } result.append(step.getName()); } if (result == null) { return "empty"; //$NON-NLS-1$ } else { return result.toString(); } } /** * Get the last path element * * @return the last path element or <code>null</code> if it's empty */ public PathElement getLastElement() { if (steps.isEmpty()) { return null; } return steps.get(steps.size() - 1); } }