/*
* citygml4j - The Open Source Java API for CityGML
* https://github.com/citygml4j
*
* Copyright 2013-2017 Claus Nagel <claus.nagel@gmail.com>
*
* 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 org.citygml4j.xml.schema;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import javax.xml.namespace.QName;
import org.citygml4j.model.module.citygml.CoreModule;
import org.citygml4j.model.module.gml.GMLCoreModule;
import org.citygml4j.model.module.gml.XLinkModule;
import com.sun.xml.xsom.XSAttributeDecl;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSType;
public class ElementDecl {
private final XSElementDecl element;
private final Schema schema;
private final EnumSet<TypeFlag> typeFlag;
private enum TypeFlag {
ABSTRACT_GML, FEATURE, FEATURE_COLLECTION, CITY_OBJECT, GEOMETRY, FEATURE_PROPERTY, GEOMETRY_PROPERTY, XLINK,
NO_ABSTRACT_GML, NO_FEATURE, NO_FEATURE_COLLECTION, NO_CITY_OBJECT, NO_GEOMETRY, NO_FEATURE_PROPERTY, NO_GEOMETRY_PROPERTY, NO_XLINK
}
public ElementDecl(XSElementDecl decl, Schema schema) {
this.element = decl;
this.schema = schema;
typeFlag = EnumSet.noneOf(TypeFlag.class);
}
public XSElementDecl getXSElementDecl() {
return element;
}
public boolean isGlobal() {
return element.isGlobal();
}
public boolean isLocal() {
return element.isLocal();
}
public boolean isAbstract() {
return element.isAbstract();
}
public String getNamespaceURI() {
return element.getTargetNamespace();
}
public String getLocalName() {
return element.getName();
}
public QName getSubsitutionGroup() {
XSElementDecl head = element.getSubstAffiliation();
return (head != null) ? new QName(head.getTargetNamespace(), head.getName()) : null;
}
public QName getRootSubsitutionGroup() {
XSElementDecl tmp = element;
XSElementDecl head = null;
while ((tmp = tmp.getSubstAffiliation()) != null)
head = tmp;
return (head != null) ? new QName(head.getTargetNamespace(), head.getName()) : null;
}
public boolean substitutes(String namespaceURI, String localName) {
XSElementDecl tmp = element;
while ((tmp = tmp.getSubstAffiliation()) != null) {
if (namespaceURI.equals(tmp.getTargetNamespace()) &&
localName.equals(tmp.getName()))
return true;
}
return false;
}
public boolean substitutesFeature() {
boolean substitutesFeature = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
switch (module.getVersion()) {
case v3_1_1:
substitutesFeature = substitutes(module.getNamespaceURI(), "_Feature");
break;
}
if (substitutesFeature)
break;
}
return substitutesFeature;
}
public boolean substitutesGeometry() {
boolean substitutesGeometry = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
switch (module.getVersion()) {
case v3_1_1:
substitutesGeometry = substitutes(module.getNamespaceURI(), "_Geometry");
break;
}
if (substitutesGeometry)
break;
}
return substitutesGeometry;
}
public boolean isAbstractGML() {
if (typeFlag.contains(TypeFlag.ABSTRACT_GML))
return true;
else if (typeFlag.contains(TypeFlag.NO_ABSTRACT_GML))
return false;
boolean isAbstractGML = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isAbstractGML = isDerivedFromComplexType(gml, "AbstractGMLType");
break;
}
if (isAbstractGML)
break;
}
if (isAbstractGML) {
typeFlag.add(TypeFlag.ABSTRACT_GML);
} else {
typeFlag.add(TypeFlag.NO_ABSTRACT_GML);
typeFlag.add(TypeFlag.NO_FEATURE);
typeFlag.add(TypeFlag.NO_FEATURE_COLLECTION);
typeFlag.add(TypeFlag.NO_CITY_OBJECT);
typeFlag.add(TypeFlag.NO_GEOMETRY);
}
return isAbstractGML;
}
public boolean isFeature() {
if (typeFlag.contains(TypeFlag.FEATURE))
return true;
else if (typeFlag.contains(TypeFlag.NO_FEATURE))
return false;
boolean isFeature = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isFeature = isDerivedFromComplexType(gml, "AbstractFeatureType");
break;
}
if (isFeature)
break;
}
if (isFeature) {
typeFlag.add(TypeFlag.ABSTRACT_GML);
typeFlag.add(TypeFlag.FEATURE);
} else {
typeFlag.add(TypeFlag.NO_FEATURE);
typeFlag.add(TypeFlag.NO_CITY_OBJECT);
}
return isFeature;
}
public boolean isFeatureCollection() {
if (typeFlag.contains(TypeFlag.FEATURE_COLLECTION))
return true;
else if (typeFlag.contains(TypeFlag.NO_FEATURE_COLLECTION))
return false;
boolean isFeatureCollection = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isFeatureCollection = isDerivedFromComplexType(gml, "AbstractFeatureCollectionType");
break;
}
if (isFeatureCollection)
break;
}
if (isFeatureCollection) {
typeFlag.add(TypeFlag.ABSTRACT_GML);
typeFlag.add(TypeFlag.FEATURE);
typeFlag.add(TypeFlag.FEATURE_COLLECTION);
} else {
typeFlag.add(TypeFlag.NO_FEATURE);
typeFlag.add(TypeFlag.NO_FEATURE_COLLECTION);
}
return isFeatureCollection;
}
public boolean isGeometry() {
if (typeFlag.contains(TypeFlag.GEOMETRY))
return true;
else if (typeFlag.contains(TypeFlag.NO_GEOMETRY))
return false;
boolean isGeometry = false;
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isGeometry = isDerivedFromComplexType(gml, "AbstractGeometryType");
break;
}
if (isGeometry)
break;
}
if (isGeometry) {
typeFlag.add(TypeFlag.ABSTRACT_GML);
typeFlag.add(TypeFlag.GEOMETRY);
} else {
typeFlag.add(TypeFlag.NO_GEOMETRY);
}
return isGeometry;
}
public boolean isCityObject() {
if (typeFlag.contains(TypeFlag.CITY_OBJECT))
return true;
else if (typeFlag.contains(TypeFlag.NO_CITY_OBJECT))
return false;
boolean isCityObject = false;
for (CoreModule module : CoreModule.getInstances()) {
XSSchema core = schema.schemaSet.getSchema(module.getNamespaceURI());
if (core == null)
continue;
isCityObject = isDerivedFromComplexType(core, "AbstractCityObjectType");
if (isCityObject)
break;
}
if (!isCityObject && schema.schemaSet.getSchema("http://www.citygml.org/citygml/1/0/0") != null)
isCityObject = isDerivedFromComplexType(schema.schemaSet.getSchema("http://www.citygml.org/citygml/1/0/0"), "_CityObjectType");
if (isCityObject) {
typeFlag.add(TypeFlag.ABSTRACT_GML);
typeFlag.add(TypeFlag.FEATURE);
typeFlag.add(TypeFlag.CITY_OBJECT);
} else {
typeFlag.add(TypeFlag.NO_CITY_OBJECT);
}
return isCityObject;
}
public boolean isFeatureProperty() {
if (typeFlag.contains(TypeFlag.FEATURE_PROPERTY))
return true;
else if (typeFlag.contains(TypeFlag.NO_FEATURE_PROPERTY))
return false;
boolean isFeatureProperty = false;
for (XSElementDecl child : getChildElements()) {
Schema childSchema = schema.handler.getSchema(child.getTargetNamespace());
List<ElementDecl> childElementDecls = childSchema.getElementDecls(child.getName());
if (childElementDecls.size() == 1) {
if (childElementDecls.get(0).isFeature())
isFeatureProperty = true;
} else {
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isFeatureProperty = child.getType().isDerivedFrom(gml.getType("AbstractFeatureType"));
break;
}
if (isFeatureProperty)
break;
}
}
if (isFeatureProperty)
break;
}
if (isFeatureProperty) {
typeFlag.add(TypeFlag.FEATURE_PROPERTY);
typeFlag.add(TypeFlag.NO_GEOMETRY_PROPERTY);
} else
typeFlag.add(TypeFlag.NO_FEATURE_PROPERTY);
return isFeatureProperty;
}
public boolean isGeometryProperty() {
if (typeFlag.contains(TypeFlag.GEOMETRY_PROPERTY))
return true;
else if (typeFlag.contains(TypeFlag.NO_GEOMETRY_PROPERTY))
return false;
boolean isGeometryProperty = false;
for (XSElementDecl child : getChildElements()) {
Schema childSchema = schema.handler.getSchema(child.getTargetNamespace());
List<ElementDecl> childElementDecls = childSchema.getElementDecls(child.getName());
if (childElementDecls.size() == 1) {
if (childElementDecls.get(0).isGeometry())
isGeometryProperty = true;
} else {
for (GMLCoreModule module : GMLCoreModule.getInstances()) {
XSSchema gml = schema.schemaSet.getSchema(module.getNamespaceURI());
if (gml == null)
continue;
switch (module.getVersion()) {
case v3_1_1:
isGeometryProperty = child.getType().isDerivedFrom(gml.getType("AbstractGeometryType"));
break;
}
if (isGeometryProperty)
break;
}
}
if (isGeometryProperty)
break;
}
if (isGeometryProperty) {
typeFlag.add(TypeFlag.GEOMETRY_PROPERTY);
typeFlag.add(TypeFlag.NO_FEATURE_PROPERTY);
} else
typeFlag.add(TypeFlag.NO_GEOMETRY_PROPERTY);
return isGeometryProperty;
}
public boolean hasXLinkAttribute() {
if (typeFlag.contains(TypeFlag.XLINK))
return true;
else if (typeFlag.contains(TypeFlag.NO_XLINK))
return false;
final boolean[] hasXLink = new boolean[1];
SchemaWalker walker = new SchemaWalker() {
@Override
public void elementDecl(XSElementDecl decl) {
}
@Override
public void attributeDecl(XSAttributeDecl decl) {
if (XLinkModule.v3_1_1.getNamespaceURI().equals(decl.getTargetNamespace()) &&
decl.getName().equals("href")) {
hasXLink[0] = true;
setShouldWalk(false);
}
}
};
element.getType().visit(walker);
if (hasXLink[0])
typeFlag.add(TypeFlag.XLINK);
else
typeFlag.add(TypeFlag.NO_XLINK);
return hasXLink[0];
}
private boolean isDerivedFromComplexType(XSSchema schema, String localPart) {
XSType base = schema.getType(localPart);
if (base != null) {
if (base.isSimpleType()) {
typeFlag.add(TypeFlag.NO_ABSTRACT_GML);
typeFlag.add(TypeFlag.NO_FEATURE);
typeFlag.add(TypeFlag.NO_FEATURE_COLLECTION);
typeFlag.add(TypeFlag.NO_CITY_OBJECT);
typeFlag.add(TypeFlag.NO_GEOMETRY);
typeFlag.add(TypeFlag.NO_FEATURE_PROPERTY);
typeFlag.add(TypeFlag.NO_GEOMETRY_PROPERTY);
return false;
} else
return element.getType().isDerivedFrom(base);
}
return false;
}
List<XSElementDecl> getChildElements() {
final List<XSElementDecl> childs = new ArrayList<XSElementDecl>();
SchemaWalker schemaWalker = new SchemaWalker() {
@Override
public void elementDecl(XSElementDecl child) {
childs.add(child);
}
};
element.getType().visit(schemaWalker);
return childs;
}
}