1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.imagery;
|
---|
3 |
|
---|
4 | import java.io.InputStream;
|
---|
5 | import java.net.MalformedURLException;
|
---|
6 | import java.net.URL;
|
---|
7 | import java.util.Locale;
|
---|
8 |
|
---|
9 | import javax.xml.namespace.QName;
|
---|
10 | import javax.xml.stream.XMLInputFactory;
|
---|
11 | import javax.xml.stream.XMLStreamException;
|
---|
12 | import javax.xml.stream.XMLStreamReader;
|
---|
13 |
|
---|
14 | import org.openstreetmap.josm.tools.Utils;
|
---|
15 |
|
---|
16 | /**
|
---|
17 | * Helper class for handling OGC GetCapabilities documents
|
---|
18 | * @since 10993
|
---|
19 | */
|
---|
20 | public final class GetCapabilitiesParseHelper {
|
---|
21 | enum TransferMode {
|
---|
22 | KVP("KVP"),
|
---|
23 | REST("RESTful");
|
---|
24 |
|
---|
25 | private final String typeString;
|
---|
26 |
|
---|
27 | TransferMode(String urlString) {
|
---|
28 | this.typeString = urlString;
|
---|
29 | }
|
---|
30 |
|
---|
31 | private String getTypeString() {
|
---|
32 | return typeString;
|
---|
33 | }
|
---|
34 |
|
---|
35 | static TransferMode fromString(String s) {
|
---|
36 | for (TransferMode type : TransferMode.values()) {
|
---|
37 | if (type.getTypeString().equals(s)) {
|
---|
38 | return type;
|
---|
39 | }
|
---|
40 | }
|
---|
41 | return null;
|
---|
42 | }
|
---|
43 | }
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * OWS namespace address
|
---|
47 | */
|
---|
48 | public static final String OWS_NS_URL = "http://www.opengis.net/ows/1.1";
|
---|
49 | /**
|
---|
50 | * XML xlink namespace address
|
---|
51 | */
|
---|
52 | public static final String XLINK_NS_URL = "http://www.w3.org/1999/xlink";
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * QNames in OWS namespace
|
---|
56 | */
|
---|
57 | // CHECKSTYLE.OFF: SingleSpaceSeparator
|
---|
58 | static final QName QN_OWS_ALLOWED_VALUES = new QName(OWS_NS_URL, "AllowedValues");
|
---|
59 | static final QName QN_OWS_CONSTRAINT = new QName(OWS_NS_URL, "Constraint");
|
---|
60 | static final QName QN_OWS_DCP = new QName(OWS_NS_URL, "DCP");
|
---|
61 | static final QName QN_OWS_GET = new QName(OWS_NS_URL, "Get");
|
---|
62 | static final QName QN_OWS_HTTP = new QName(OWS_NS_URL, "HTTP");
|
---|
63 | static final QName QN_OWS_IDENTIFIER = new QName(OWS_NS_URL, "Identifier");
|
---|
64 | static final QName QN_OWS_OPERATION = new QName(OWS_NS_URL, "Operation");
|
---|
65 | static final QName QN_OWS_OPERATIONS_METADATA = new QName(OWS_NS_URL, "OperationsMetadata");
|
---|
66 | static final QName QN_OWS_SUPPORTED_CRS = new QName(OWS_NS_URL, "SupportedCRS");
|
---|
67 | static final QName QN_OWS_TITLE = new QName(OWS_NS_URL, "Title");
|
---|
68 | static final QName QN_OWS_VALUE = new QName(OWS_NS_URL, "Value");
|
---|
69 | // CHECKSTYLE.ON: SingleSpaceSeparator
|
---|
70 |
|
---|
71 | private GetCapabilitiesParseHelper() {
|
---|
72 | // Hide default constructor for utilities classes
|
---|
73 | }
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * @param in InputStream with pointing to GetCapabilities XML stream
|
---|
77 | * @return safe XMLStreamReader, that is not validating external entities, nor loads DTD's
|
---|
78 | * @throws XMLStreamException if any XML stream error occurs
|
---|
79 | */
|
---|
80 | public static XMLStreamReader getReader(InputStream in) throws XMLStreamException {
|
---|
81 | XMLInputFactory factory = XMLInputFactory.newInstance();
|
---|
82 | // do not try to load external entities, nor validate the XML
|
---|
83 | factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
|
---|
84 | factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
|
---|
85 | factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
|
---|
86 | return factory.createXMLStreamReader(in);
|
---|
87 | }
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * Moves the reader to the closing tag of current tag.
|
---|
91 | * @param reader XMLStreamReader which should be moved
|
---|
92 | * @throws XMLStreamException when parse exception occurs
|
---|
93 | */
|
---|
94 | public static void moveReaderToEndCurrentTag(XMLStreamReader reader) throws XMLStreamException {
|
---|
95 | int level = 0;
|
---|
96 | QName tag = reader.getName();
|
---|
97 | for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) {
|
---|
98 | if (XMLStreamReader.START_ELEMENT == event) {
|
---|
99 | level += 1;
|
---|
100 | } else if (XMLStreamReader.END_ELEMENT == event) {
|
---|
101 | level -= 1;
|
---|
102 | if (level == 0 && tag.equals(reader.getName())) {
|
---|
103 | return;
|
---|
104 | }
|
---|
105 | }
|
---|
106 | if (level < 0) {
|
---|
107 | throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag");
|
---|
108 | }
|
---|
109 | }
|
---|
110 | throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag");
|
---|
111 | }
|
---|
112 |
|
---|
113 | /**
|
---|
114 | * Moves reader to first occurrence of the structure equivalent of Xpath tags[0]/tags[1]../tags[n]. If fails to find
|
---|
115 | * moves the reader to the closing tag of current tag
|
---|
116 | *
|
---|
117 | * @param tags array of tags
|
---|
118 | * @param reader XMLStreamReader which should be moved
|
---|
119 | * @return true if tag was found, false otherwise
|
---|
120 | * @throws XMLStreamException See {@link XMLStreamReader}
|
---|
121 | */
|
---|
122 | public static boolean moveReaderToTag(XMLStreamReader reader, QName... tags) throws XMLStreamException {
|
---|
123 | QName stopTag = reader.getName();
|
---|
124 | int currentLevel = 0;
|
---|
125 | QName searchTag = tags[currentLevel];
|
---|
126 | QName parentTag = null;
|
---|
127 | QName skipTag = null;
|
---|
128 |
|
---|
129 | for (int event = 0; //skip current element, so we will not skip it as a whole
|
---|
130 | reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && stopTag.equals(reader.getName()));
|
---|
131 | event = reader.next()) {
|
---|
132 | if (event == XMLStreamReader.END_ELEMENT && skipTag != null && skipTag.equals(reader.getName())) {
|
---|
133 | skipTag = null;
|
---|
134 | }
|
---|
135 | if (skipTag == null) {
|
---|
136 | if (event == XMLStreamReader.START_ELEMENT) {
|
---|
137 | if (searchTag.equals(reader.getName())) {
|
---|
138 | currentLevel += 1;
|
---|
139 | if (currentLevel >= tags.length) {
|
---|
140 | return true; // found!
|
---|
141 | }
|
---|
142 | parentTag = searchTag;
|
---|
143 | searchTag = tags[currentLevel];
|
---|
144 | } else {
|
---|
145 | skipTag = reader.getName();
|
---|
146 | }
|
---|
147 | }
|
---|
148 |
|
---|
149 | if (event == XMLStreamReader.END_ELEMENT && parentTag != null && parentTag.equals(reader.getName())) {
|
---|
150 | currentLevel -= 1;
|
---|
151 | searchTag = parentTag;
|
---|
152 | if (currentLevel >= 0) {
|
---|
153 | parentTag = tags[currentLevel];
|
---|
154 | } else {
|
---|
155 | parentTag = null;
|
---|
156 | }
|
---|
157 | }
|
---|
158 | }
|
---|
159 | }
|
---|
160 | return false;
|
---|
161 | }
|
---|
162 |
|
---|
163 | /**
|
---|
164 | * Parses Operation[@name='GetTile']/DCP/HTTP/Get section. Returns when reader is on Get closing tag.
|
---|
165 | * @param reader StAX reader instance
|
---|
166 | * @return TransferMode coded in this section
|
---|
167 | * @throws XMLStreamException See {@link XMLStreamReader}
|
---|
168 | */
|
---|
169 | public static TransferMode getTransferMode(XMLStreamReader reader) throws XMLStreamException {
|
---|
170 | QName getQname = QN_OWS_GET;
|
---|
171 |
|
---|
172 | Utils.ensure(getQname.equals(reader.getName()), "WMTS Parser state invalid. Expected element %s, got %s",
|
---|
173 | getQname, reader.getName());
|
---|
174 | for (int event = reader.getEventType();
|
---|
175 | reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && getQname.equals(reader.getName()));
|
---|
176 | event = reader.next()) {
|
---|
177 | if (event == XMLStreamReader.START_ELEMENT && QN_OWS_CONSTRAINT.equals(reader.getName())
|
---|
178 | && "GetEncoding".equals(reader.getAttributeValue("", "name"))) {
|
---|
179 | moveReaderToTag(reader, QN_OWS_ALLOWED_VALUES, QN_OWS_VALUE);
|
---|
180 | return TransferMode.fromString(reader.getElementText());
|
---|
181 | }
|
---|
182 | }
|
---|
183 | return null;
|
---|
184 | }
|
---|
185 |
|
---|
186 | /**
|
---|
187 | * @param url URL
|
---|
188 | * @return normalized URL
|
---|
189 | * @throws MalformedURLException in case of malformed URL
|
---|
190 | * @since 10993
|
---|
191 | */
|
---|
192 | public static String normalizeCapabilitiesUrl(String url) throws MalformedURLException {
|
---|
193 | URL inUrl = new URL(url);
|
---|
194 | URL ret = new URL(inUrl.getProtocol(), inUrl.getHost(), inUrl.getPort(), inUrl.getFile());
|
---|
195 | return ret.toExternalForm();
|
---|
196 | }
|
---|
197 |
|
---|
198 | /**
|
---|
199 | * Convert CRS identifier to plain code
|
---|
200 | * @param crsIdentifier CRS identifier
|
---|
201 | * @return CRS Identifier as it is used within JOSM (without prefix)
|
---|
202 | * @see <a href="https://portal.opengeospatial.org/files/?artifact_id=24045">
|
---|
203 | * Definition identifier URNs in OGC namespace, chapter 7.2: URNs for single objects</a>
|
---|
204 | */
|
---|
205 | public static String crsToCode(String crsIdentifier) {
|
---|
206 | if (crsIdentifier.startsWith("urn:ogc:def:crs:")) {
|
---|
207 | return crsIdentifier.replaceFirst("urn:ogc:def:crs:([^:]*)(?::.*)?:(.*)$", "$1:$2").toUpperCase(Locale.ENGLISH);
|
---|
208 | }
|
---|
209 | return crsIdentifier;
|
---|
210 | }
|
---|
211 |
|
---|
212 | }
|
---|