/* * 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.text.MessageFormat; import java.util.List; import java.util.ListIterator; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import eu.esdihumboldt.hale.io.gml.writer.internal.GmlWriterUtil; /** * Represents a descent in the document, must be used to end elements started * with * * @author Simon Templer * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public class Descent { private final XMLStreamWriter writer; private final DefinitionPath path; /** * Constructor * * @param writer the XMl stream writer * @param path the descent path */ private Descent(XMLStreamWriter writer, DefinitionPath path) { super(); this.path = path; this.writer = writer; } /** * @return the path */ public DefinitionPath getPath() { return path; } /** * Close the descent * * @throws XMLStreamException if an error occurs closing the elements */ public void close() throws XMLStreamException { if (path.isEmpty()) { return; } for (int i = 0; i < path.getSteps().size(); i++) { PathElement step = path.getSteps().get(path.getSteps().size() - 1 - i); if (!step.isTransient()) { writer.writeEndElement(); } } } /** * Descend the given path * * @param writer the XML stream writer * @param descendPath the path to descend * @param previousDescent the previous descent, that will be closed or * partially closed as needed, may be <code>null</code> * @param generateRequiredIDs if required IDs shall be generated for the * path elements * @return the descent that was opened, it must be closed to close the * opened elements * @throws XMLStreamException if an error occurs writing the coordinates */ public static Descent descend(XMLStreamWriter writer, DefinitionPath descendPath, Descent previousDescent, boolean generateRequiredIDs) throws XMLStreamException { return descend(writer, descendPath, previousDescent, generateRequiredIDs, false); } /** * Descend the given path * * @param writer the XML stream writer * @param descendPath the path to descend * @param previousDescent the previous descent, that will be closed or * partially closed as needed, may be <code>null</code> * @param generateRequiredIDs if required IDs shall be generated for the * path elements * @param allowFullClose if it is allowed to fully close the previous * descent regardless of element uniqueness * @return the descent that was opened, it must be closed to close the * opened elements * @throws XMLStreamException if an error occurs writing the coordinates */ public static Descent descend(XMLStreamWriter writer, DefinitionPath descendPath, Descent previousDescent, boolean generateRequiredIDs, boolean allowFullClose) throws XMLStreamException { if (descendPath.isEmpty()) { if (previousDescent != null) { // close previous descent previousDescent.close(); } return new Descent(writer, descendPath); } List<PathElement> stepDown = descendPath.getSteps(); PathElement downFrom = null; PathElement downAfter = null; if (previousDescent != null) { List<PathElement> previousSteps = previousDescent.getPath().getSteps(); // find the first non-unique match in both paths PathElement firstNonUniqueMatch = null; int index = 0; for (PathElement step : previousSteps) { // check from the beginning how far the paths match if (stepDown.size() > index && stepDown.get(index).equals(step)) { if (!step.isUnique()) { firstNonUniqueMatch = step; // found the first non-unique match break; } } else { // after the first miss no more valid matches can be found break; } index++; } // close previous descent as needed ListIterator<PathElement> itPrev = previousSteps.listIterator(previousSteps.size()); if (!allowFullClose && firstNonUniqueMatch == null) { boolean endedSomething = false; while (itPrev.hasPrevious()) { PathElement step = itPrev.previous(); if (stepDown.contains(step)) { // step is contained in both paths if (step.isUnique()) { // step may not be closed, as the next path also // wants to enter // from the next path all steps before and including // this step must be ignored for stepping down downAfter = step; break; } } // close step if (!step.isTransient()) { writer.writeEndElement(); endedSomething = true; } } if (!endedSomething) { throw new IllegalStateException( MessageFormat .format("Previous path ''{0}'' has only unique common elements with path ''{1}'', therefore a sequence of both is not possible", previousDescent.getPath().toString(), descendPath.toString())); } } else { while (itPrev.hasPrevious()) { PathElement step = itPrev.previous(); if (!step.isTransient()) { // close step writer.writeEndElement(); } if (firstNonUniqueMatch != null && firstNonUniqueMatch.equals(step)) { // step after this may not be closed, as the next path // also wants to enter // from the next path all steps before this step must be // ignored for stepping down downFrom = step; break; } } } } for (PathElement step : stepDown) { if (downFrom != null && downFrom.equals(step)) { downFrom = null; } if (downFrom == null && downAfter == null) { if (!step.isTransient()) { // start elements GmlWriterUtil.writeStartPathElement(writer, step, generateRequiredIDs); } } if (downAfter != null && downAfter.equals(step)) { downAfter = null; } } return new Descent(writer, descendPath); } }