// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.data.imagery; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.openstreetmap.josm.tools.Utils; /** * Helper class for handling OGC GetCapabilities documents * */ public final class GetCapabilitiesParseHelper { enum TransferMode { KVP("KVP"), REST("RESTful"); private final String typeString; TransferMode(String urlString) { this.typeString = urlString; } private String getTypeString() { return typeString; } static TransferMode fromString(String s) { for (TransferMode type : TransferMode.values()) { if (type.getTypeString().equals(s)) { return type; } } return null; } } /** * OWS namespace address */ public static final String OWS_NS_URL = "http://www.opengis.net/ows/1.1"; /** * XML xlink namespace address */ public static final String XLINK_NS_URL = "http://www.w3.org/1999/xlink"; /** * QNames in OWS namespace */ // CHECKSTYLE.OFF: SingleSpaceSeparator static final QName QN_OWS_ALLOWED_VALUES = new QName(OWS_NS_URL, "AllowedValues"); static final QName QN_OWS_CONSTRAINT = new QName(OWS_NS_URL, "Constraint"); static final QName QN_OWS_DCP = new QName(OWS_NS_URL, "DCP"); static final QName QN_OWS_GET = new QName(OWS_NS_URL, "Get"); static final QName QN_OWS_HTTP = new QName(OWS_NS_URL, "HTTP"); static final QName QN_OWS_IDENTIFIER = new QName(OWS_NS_URL, "Identifier"); static final QName QN_OWS_OPERATION = new QName(OWS_NS_URL, "Operation"); static final QName QN_OWS_OPERATIONS_METADATA = new QName(OWS_NS_URL, "OperationsMetadata"); static final QName QN_OWS_SUPPORTED_CRS = new QName(OWS_NS_URL, "SupportedCRS"); static final QName QN_OWS_TITLE = new QName(OWS_NS_URL, "Title"); static final QName QN_OWS_VALUE = new QName(OWS_NS_URL, "Value"); // CHECKSTYLE.ON: SingleSpaceSeparator private GetCapabilitiesParseHelper() { // Hide default constructor for utilities classes } /** * @param in InputStream with pointing to GetCapabilities XML stream * @return safe XMLStreamReader, that is not validating external entities, nor loads DTD's * @throws XMLStreamException if any XML stream error occurs */ public static XMLStreamReader getReader(InputStream in) throws XMLStreamException { XMLInputFactory factory = XMLInputFactory.newFactory(); // do not try to load external entities, nor validate the XML factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); return factory.createXMLStreamReader(in); } /** * Moves the reader to the closing tag of current tag. * @param reader XMLStreamReader which should be moved * @throws XMLStreamException when parse exception occurs */ public static void moveReaderToEndCurrentTag(XMLStreamReader reader) throws XMLStreamException { int level = 0; QName tag = reader.getName(); for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) { if (XMLStreamReader.START_ELEMENT == event) { level += 1; } else if (XMLStreamReader.END_ELEMENT == event) { level -= 1; if (level == 0 && tag.equals(reader.getName())) { return; } } if (level < 0) { throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag"); } } throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag"); } /** * Moves reader to first occurrence of the structure equivalent of Xpath tags[0]/tags[1]../tags[n]. If fails to find * moves the reader to the closing tag of current tag * * @param tags array of tags * @param reader XMLStreamReader which should be moved * @return true if tag was found, false otherwise * @throws XMLStreamException See {@link XMLStreamReader} */ public static boolean moveReaderToTag(XMLStreamReader reader, QName... tags) throws XMLStreamException { QName stopTag = reader.getName(); int currentLevel = 0; QName searchTag = tags[currentLevel]; QName parentTag = null; QName skipTag = null; for (int event = 0; //skip current element, so we will not skip it as a whole reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && stopTag.equals(reader.getName())); event = reader.next()) { if (event == XMLStreamReader.END_ELEMENT && skipTag != null && skipTag.equals(reader.getName())) { skipTag = null; } if (skipTag == null) { if (event == XMLStreamReader.START_ELEMENT) { if (searchTag.equals(reader.getName())) { currentLevel += 1; if (currentLevel >= tags.length) { return true; // found! } parentTag = searchTag; searchTag = tags[currentLevel]; } else { skipTag = reader.getName(); } } if (event == XMLStreamReader.END_ELEMENT && parentTag != null && parentTag.equals(reader.getName())) { currentLevel -= 1; searchTag = parentTag; if (currentLevel >= 0) { parentTag = tags[currentLevel]; } else { parentTag = null; } } } } return false; } /** * Parses Operation[@name='GetTile']/DCP/HTTP/Get section. Returns when reader is on Get closing tag. * @param reader StAX reader instance * @return TransferMode coded in this section * @throws XMLStreamException See {@link XMLStreamReader} */ public static TransferMode getTransferMode(XMLStreamReader reader) throws XMLStreamException { QName getQname = QN_OWS_GET; Utils.ensure(getQname.equals(reader.getName()), "WMTS Parser state invalid. Expected element %s, got %s", getQname, reader.getName()); for (int event = reader.getEventType(); reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && getQname.equals(reader.getName())); event = reader.next()) { if (event == XMLStreamReader.START_ELEMENT && QN_OWS_CONSTRAINT.equals(reader.getName()) && "GetEncoding".equals(reader.getAttributeValue("", "name"))) { moveReaderToTag(reader, new QName[]{ QN_OWS_ALLOWED_VALUES, QN_OWS_VALUE }); return TransferMode.fromString(reader.getElementText()); } } return null; } /** * @param url URL * @return normalized URL * @throws MalformedURLException in case of malformed URL * @since 10993 */ public static String normalizeCapabilitiesUrl(String url) throws MalformedURLException { URL inUrl = new URL(url); URL ret = new URL(inUrl.getProtocol(), inUrl.getHost(), inUrl.getPort(), inUrl.getFile()); return ret.toExternalForm(); } /** * Convert CRS identifier to plain code * @param crsIdentifier CRS identifier * @return CRS Identifier as it is used within JOSM (without prefix) */ public static String crsToCode(String crsIdentifier) { if (crsIdentifier.startsWith("urn:ogc:def:crs:")) { return crsIdentifier.replaceFirst("urn:ogc:def:crs:([^:]*):.*:(.*)$", "$1:$2"); } return crsIdentifier; } }