/* * 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.reader; import java.io.InputStream; import java.net.URI; import java.util.NoSuchElementException; import java.util.Stack; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.citygml4j.builder.jaxb.xml.io.reader.XMLElementChecker.ElementInfo; import org.citygml4j.model.citygml.CityGML; import org.citygml4j.model.module.gml.GMLCoreModule; import org.citygml4j.model.module.gml.XLinkModule; import org.citygml4j.xml.io.reader.CityGMLReadException; import org.citygml4j.xml.io.reader.CityGMLReader; import org.citygml4j.xml.io.reader.MissingADESchemaException; import org.citygml4j.xml.io.reader.ParentInfo; import org.citygml4j.xml.io.reader.UnmarshalException; import org.citygml4j.xml.io.reader.XMLChunk; import org.xml.sax.SAXException; public class JAXBChunkReader extends AbstractJAXBReader implements CityGMLReader { private XMLChunkImpl current; private XMLChunkImpl iterator; private Stack<XMLChunkImpl> chunks; private XMLChunkImpl chunk; private ElementInfo elementInfo; private Stack<ElementInfo> elementInfos; private boolean isInited = false; private boolean setXLink = false; public JAXBChunkReader(XMLStreamReader reader, InputStream in, JAXBInputFactory factory, URI baseURI) throws CityGMLReadException { super(reader, in, factory, baseURI); jaxbUnmarshaller.setParseSchema(false); chunks = new Stack<XMLChunkImpl>(); elementInfos = new Stack<ElementInfo>(); } public void close() throws CityGMLReadException { super.close(); current = null; iterator = null; chunks.clear(); chunk = null; elementInfos.clear(); elementInfo = null; } public synchronized boolean hasNext() throws CityGMLReadException { if (iterator == null) { try { iterator = (XMLChunkImpl)nextChunk(); } catch (NoSuchElementException e) { // } } return iterator != null; } public XMLChunk nextChunk() throws CityGMLReadException { if (iterator == null) { try { current = null; while (reader.hasNext()) { int event = reader.next(); // keep track of schema documents if (event == XMLStreamConstants.START_ELEMENT && parseSchema) { for (int i = 0; i < reader.getAttributeCount(); i++) { if (reader.getAttributeLocalName(i).equals("schemaLocation")) parseSchema(reader.getAttributeValue(i)); else if (reader.getAttributeLocalName(i).equals("noNamespaceSchemaLocation")) schemaHandler.parseSchema("", reader.getAttributeValue(i)); } } // move to the next CityGML feature in the document // and create a new chunk to initiate parsing if (!isInited) { if (event != XMLStreamConstants.START_ELEMENT) continue; else { elementInfo = elementChecker.getCityGMLFeature(reader.getName(), isFilteredReader()); if (elementInfo != null && elementInfo.isFeature()) { isInited = true; chunks.clear(); chunk = new XMLChunkImpl(this, null, elementInfo.getType()); chunk.setFirstElement(reader.getName()); if (isFilteredReader()) chunk.setIsFiltered(!filter.accept(elementInfo.getType())); } else continue; } } // check whether start element is a feature if (event == XMLStreamConstants.START_ELEMENT) { ElementInfo lastElementInfo = elementInfos.push(elementInfo); elementInfo = elementChecker.getElementInfo(reader.getName(), chunk, lastElementInfo, isFilteredReader()); if (elementInfo != null && elementInfo.isFeature()) { chunk.removeTrailingCharacters(); chunks.add(chunk); chunk = new XMLChunkImpl(this, chunks.peek(), elementInfo.getType()); chunk.setFirstElement(reader.getName()); if (isFilteredReader()) chunk.setIsFiltered(!filter.accept(elementInfo.getType())); if (lastElementInfo != null) setXLink = lastElementInfo.hasXLink(); } } // pop last element info else if (event == XMLStreamConstants.END_ELEMENT) elementInfo = elementInfos.pop(); // add streaming event to the current chunk chunk.addEvent(reader); if (setXLink) setXLink(reader); // if the chunk is complete, return it if (chunk.isComplete()) { current = chunk; if (!chunks.isEmpty()) chunk = chunks.pop(); else { chunk = null; isInited = false; } if (!isFilteredReader() || !current.isFiltered()) break; else current = null; } } } catch (XMLStreamException e) { throw new CityGMLReadException("Caused by: ", e); } catch (SAXException e) { throw new CityGMLReadException("Caused by: ", e); } catch (MissingADESchemaException e) { throw new CityGMLReadException("Caused by: ", e); } if (current == null) throw new NoSuchElementException(); } else current = iterator; iterator = null; return current; } public CityGML nextFeature() throws CityGMLReadException { CityGML cityGML = null; XMLChunkImpl next = (XMLChunkImpl)nextChunk(); try { cityGML = next.unmarshal(); } catch (UnmarshalException e) { throw new CityGMLReadException("Caused by: ", e.getCause()); } catch (MissingADESchemaException e) { throw new CityGMLReadException("Caused by: ", e); } return cityGML; } private void setXLink(XMLStreamReader reader) { setXLink = false; // check whether the new chunk has a gml:id String gmlId = null; for (int i = 0; i < reader.getAttributeCount(); i++) { if (reader.getAttributeLocalName(i).equals("id")) { gmlId = reader.getAttributeValue(i); break; } } // set gml:id if not present if (gmlId == null) { gmlId = factory.getGMLIdManager().generateUUID(); chunk.addAttribute(GMLCoreModule.v3_1_1.getNamespaceURI(), "id", "id", "CDATA", gmlId); } // set xlink:href property on previous chunk chunks.peek().addAttribute(XLinkModule.v3_1_1.getNamespaceURI(), "href", "href", "CDATA", '#' + gmlId); } public boolean isSetParentInfo() { return getParentInfo() != null; } public ParentInfo getParentInfo() { return current != null ? current.getParentInfo() : null; } }