/*
* 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.builder.jaxb.xml.io.writer;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXResult;
import org.citygml4j.model.citygml.CityGML;
import org.citygml4j.model.citygml.ade.ADEComponent;
import org.citygml4j.model.citygml.appearance.Appearance;
import org.citygml4j.model.citygml.appearance.AppearanceMember;
import org.citygml4j.model.citygml.core.AbstractCityObject;
import org.citygml4j.model.citygml.core.CityModel;
import org.citygml4j.model.citygml.core.CityObjectMember;
import org.citygml4j.model.common.base.ModelObject;
import org.citygml4j.model.gml.feature.AbstractFeature;
import org.citygml4j.model.gml.feature.FeatureArrayProperty;
import org.citygml4j.model.gml.feature.FeatureMember;
import org.citygml4j.model.gml.feature.FeatureProperty;
import org.citygml4j.model.module.ModuleContext;
import org.citygml4j.model.module.citygml.CityGMLModuleType;
import org.citygml4j.util.internal.xml.TransformerChain;
import org.citygml4j.util.xml.SAXFragmentWriter;
import org.citygml4j.util.xml.SAXFragmentWriter.WriteMode;
import org.citygml4j.util.xml.SAXWriter;
import org.citygml4j.xml.io.writer.CityGMLWriteException;
import org.citygml4j.xml.io.writer.CityModelInfo;
import org.citygml4j.xml.io.writer.CityModelWriter;
import org.citygml4j.xml.io.writer.FeatureWriteMode;
import org.xml.sax.SAXException;
public class JAXBModelWriter extends AbstractJAXBWriter implements CityModelWriter {
private CityModelInfo cityModelInfo;
private ModuleContext initModuleCtx;
private DocumentState documentState = DocumentState.INITIAL;
private enum DocumentState {
INITIAL,
START_DOCUMENT,
END_DOCUMENT,
}
public JAXBModelWriter(SAXWriter writer,
JAXBOutputFactory factory,
ModuleContext moduleContext) throws CityGMLWriteException {
super(writer, factory, moduleContext);
initModuleCtx = new ModuleContext(moduleContext);
}
public JAXBModelWriter(SAXWriter writer,
JAXBOutputFactory factory,
ModuleContext moduleContext,
CityModelInfo cityModelInfo) throws CityGMLWriteException {
this(writer, factory, moduleContext);
this.cityModelInfo = cityModelInfo;
}
@Override
public void reset() {
documentState = DocumentState.INITIAL;
cityModelInfo = null;
super.reset();
}
@Override
public void close() throws CityGMLWriteException {
if (documentState == DocumentState.START_DOCUMENT)
writeEndDocument();
cityModelInfo = null;
initModuleCtx = null;
super.close();
}
public CityModelInfo getCityModelInfo() {
return cityModelInfo;
}
public void setCityModelInfo(CityModelInfo cityModelInfo) {
if (cityModelInfo == null)
throw new IllegalArgumentException("CityModelInfo may not be null.");
if (documentState == DocumentState.INITIAL)
this.cityModelInfo = cityModelInfo;
}
public void writeFeatureMember(AbstractFeature feature) throws CityGMLWriteException {
writeModelMember(feature);
}
public void writeFeatureMember(ADEComponent adeComponent) throws CityGMLWriteException {
writeModelMember(adeComponent);
}
public void writeStartDocument() throws CityGMLWriteException {
switch (documentState) {
case START_DOCUMENT:
throw new IllegalStateException("CityModel start element can only be written once.");
case END_DOCUMENT:
throw new IllegalStateException("CityModel start element cannot be written after CityModel end element.");
case INITIAL:
break;
default:
throw new IllegalStateException("Unknown document state '" + documentState + "'");
}
try {
CityModel cityModel = null;
if (cityModelInfo != null) {
cityModel = cityModelInfo.toCityModel();
cityModel.unsetGenericApplicationPropertyOfCityModel();
cityModel.unsetGenericADEComponent();
} else
cityModel = new CityModel();
JAXBElement<?> jaxbElement = jaxbMarshaller.marshalJAXBElement(cityModel);
if (jaxbElement != null) {
Marshaller marshaller = createMarshaller(false);
SAXFragmentWriter fragmentWriter = new SAXFragmentWriter(
new QName(jaxbMarshaller.getModuleContext().getModule(CityGMLModuleType.CORE).getNamespaceURI(), "CityModel"), writer, WriteMode.HEAD);
if (transformerChainFactory == null)
marshaller.marshal(jaxbElement, fragmentWriter);
else {
// apply transformation before marshalling
TransformerChain chain = transformerChainFactory.buildChain();
chain.tail().setResult(new SAXResult(fragmentWriter));
marshaller.marshal(jaxbElement, chain.head());
}
}
documentState = DocumentState.START_DOCUMENT;
} catch (JAXBException | TransformerConfigurationException e) {
throw new CityGMLWriteException("Caused by: ", e);
}
}
public void writeEndDocument() throws CityGMLWriteException {
switch (documentState) {
case END_DOCUMENT:
throw new IllegalStateException("CityModel end element can only be written once.");
case INITIAL:
writeStartDocument();
case START_DOCUMENT:
break;
default:
throw new IllegalStateException("Unknown document state '" + documentState + "'");
}
try {
CityModel cityModel = new CityModel();
if (cityModelInfo != null) {
if (cityModelInfo.isSetGenericApplicationPropertyOfCityModel())
cityModel.setGenericApplicationPropertyOfCityModel(
cityModelInfo.getGenericApplicationPropertyOfCityModel());
if (cityModelInfo.isSetGenericADEComponent())
cityModel.setGenericADEComponent(
cityModelInfo.getGenericADEComponent());
}
ModuleContext tmp = jaxbMarshaller.getModuleContext();
jaxbMarshaller.setModuleContext(initModuleCtx);
JAXBElement<?> jaxbElement = jaxbMarshaller.marshalJAXBElement(cityModel);
if (jaxbElement != null) {
SAXFragmentWriter fragmentWriter = new SAXFragmentWriter(
new QName(jaxbMarshaller.getModuleContext().getModule(CityGMLModuleType.CORE).getNamespaceURI(), "CityModel"), writer, WriteMode.TAIL);
createMarshaller(true).marshal(jaxbElement, fragmentWriter);
}
jaxbMarshaller.setModuleContext(tmp);
documentState = DocumentState.END_DOCUMENT;
} catch (JAXBException e) {
throw new CityGMLWriteException("Caused by: ", e);
}
}
private void writeModelMember(ModelObject object) throws CityGMLWriteException {
switch (documentState) {
case END_DOCUMENT:
throw new IllegalStateException("model member cannot be written after CityModel end element.");
case INITIAL:
writeStartDocument();
break;
case START_DOCUMENT:
break;
default:
throw new IllegalStateException("Unknown document state '" + documentState + "'");
}
try {
Marshaller marshaller = createMarshaller(true);
if (featureWriteMode == FeatureWriteMode.SPLIT_PER_COLLECTION_MEMBER) {
for (FeatureProperty<? extends AbstractFeature> member : split(object)) {
JAXBElement<?> jaxbElement = jaxbMarshaller.marshalJAXBElement(member);
if (jaxbElement != null) {
if (transformerChainFactory == null)
marshaller.marshal(jaxbElement, writer);
else {
// apply transformation before marshalling
TransformerChain chain = transformerChainFactory.buildChain();
chain.tail().setResult(new SAXResult(writer));
chain.head().startDocument();
marshaller.marshal(jaxbElement, chain.head());
chain.head().endDocument();
}
}
}
}
else {
FeatureProperty<? extends AbstractFeature> member = wrap(object);
if (member != null) {
JAXBElement<?> jaxbElement = jaxbMarshaller.marshalJAXBElement(member);
if (jaxbElement != null) {
if (transformerChainFactory == null)
marshaller.marshal(jaxbElement, writer);
else {
// apply transformation before marshalling
TransformerChain chain = transformerChainFactory.buildChain();
chain.tail().setResult(new SAXResult(writer));
chain.head().startDocument();
marshaller.marshal(jaxbElement, chain.head());
chain.head().endDocument();
}
}
}
}
} catch (JAXBException | SAXException | TransformerConfigurationException e) {
throw new CityGMLWriteException("Caused by: ", e);
}
}
public void writeFeatureMembers(List<ModelObject> features) throws CityGMLWriteException {
switch (documentState) {
case END_DOCUMENT:
throw new IllegalStateException("CityModel members cannot be written after document end.");
case INITIAL:
writeStartDocument();
break;
case START_DOCUMENT:
break;
default:
throw new IllegalStateException("Unknown document state '" + documentState + "'");
}
FeatureArrayProperty members = new FeatureArrayProperty();
List<FeatureProperty<? extends AbstractFeature>> featureArray = new ArrayList<FeatureProperty<? extends AbstractFeature>>();
for (ModelObject feature : features) {
if (feature instanceof AbstractFeature || feature instanceof ADEComponent) {
if (featureWriteMode == FeatureWriteMode.SPLIT_PER_COLLECTION_MEMBER)
featureArray.addAll(split(feature));
else
featureArray.add(wrap(feature));
}
}
for (FeatureProperty<? extends AbstractFeature> member : featureArray) {
if (member != null) {
if (member.isSetFeature())
members.addFeature(member.getFeature());
else if (member.isSetGenericADEComponent())
members.addGenericADEComponent(member.getGenericADEComponent());
}
}
try {
JAXBElement<?> jaxbElement = jaxbMarshaller.marshalJAXBElement(members);
if (jaxbElement != null) {
Marshaller marshaller = createMarshaller(true);
if (transformerChainFactory == null)
marshaller.marshal(jaxbElement, writer);
else {
// apply transformation before marshalling
TransformerChain chain = transformerChainFactory.buildChain();
chain.tail().setResult(new SAXResult(writer));
chain.head().startDocument();
marshaller.marshal(jaxbElement, chain.head());
chain.head().endDocument();
}
}
} catch (JAXBException | SAXException | TransformerConfigurationException e) {
throw new CityGMLWriteException("Caused by: ", e);
}
}
private FeatureProperty<? extends AbstractFeature> wrap(ModelObject object) {
FeatureProperty<? extends AbstractFeature> member = null;
if (object instanceof AbstractCityObject) {
member = new CityObjectMember();
((CityObjectMember)member).setCityObject((AbstractCityObject)object);
}
else if (object instanceof Appearance) {
member = new AppearanceMember();
((AppearanceMember)member).setAppearance((Appearance)object);
}
else if (object instanceof AbstractFeature) {
member = new FeatureMember();
((FeatureMember)member).setFeature((AbstractFeature)object);
}
else if (object instanceof ADEComponent) {
ADEComponent ade = (ADEComponent)object;
member = (isCityObject(ade)) ? new CityObjectMember() : new FeatureMember();
member.setGenericADEComponent(ade);
}
return member;
}
private List<FeatureProperty<? extends AbstractFeature>> split(ModelObject object) {
List<FeatureProperty<? extends AbstractFeature>> memberList = new ArrayList<FeatureProperty<? extends AbstractFeature>>();
List<CityGML> results = featureSplitter.split(object);
if (!results.isEmpty()) {
for (CityGML result : results) {
FeatureProperty<? extends AbstractFeature> member = wrap(result);
if (member != null)
memberList.add(member);
}
} else {
FeatureProperty<? extends AbstractFeature> member = wrap(object);
if (member != null)
memberList.add(member);
}
return memberList;
}
private Marshaller createMarshaller(boolean fragment) throws CityGMLWriteException {
try {
Marshaller marshaller = jaxbContext.createMarshaller();
if (fragment)
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
// turn validation on
if (useValidation) {
marshaller.setSchema(validationSchemaHandler.getSchema());
if (validationEventHandler != null)
marshaller.setEventHandler(validationEventHandler);
}
return marshaller;
} catch (JAXBException e) {
throw new CityGMLWriteException("Caused by: ", e);
} catch (SAXException e) {
throw new CityGMLWriteException("Caused by: ", e);
}
}
}