[5798] | 1 | /** |
---|
[7274] | 2 | * This software is copyright (c) 2012-2022 by |
---|
| 3 | * - Leibniz-Institut fuer Deutsche Sprache (http://www.ids-mannheim.de) |
---|
[5798] | 4 | * This is free software. You can redistribute it |
---|
| 5 | * and/or modify it under the terms described in |
---|
| 6 | * the GNU General Public License v3 of which you |
---|
| 7 | * should have received a copy. Otherwise you can download |
---|
| 8 | * it from |
---|
| 9 | * |
---|
| 10 | * http://www.gnu.org/licenses/gpl-3.0.txt |
---|
| 11 | * |
---|
[7274] | 12 | * @copyright Leibniz-Institut fuer Deutsche Sprache (http://www.ids-mannheim.de) |
---|
[5798] | 13 | * |
---|
| 14 | * @license http://www.gnu.org/licenses/gpl-3.0.txt |
---|
| 15 | * GNU General Public License v3 |
---|
| 16 | */ |
---|
| 17 | package eu.clarin.sru.client.fcs; |
---|
| 18 | |
---|
| 19 | import java.net.URI; |
---|
| 20 | import java.net.URISyntaxException; |
---|
| 21 | import java.util.ArrayList; |
---|
| 22 | import java.util.HashMap; |
---|
| 23 | import java.util.List; |
---|
| 24 | import java.util.Map; |
---|
| 25 | |
---|
| 26 | import javax.xml.XMLConstants; |
---|
| 27 | import javax.xml.namespace.QName; |
---|
| 28 | import javax.xml.stream.XMLStreamException; |
---|
| 29 | import javax.xml.stream.XMLStreamReader; |
---|
| 30 | |
---|
| 31 | import org.slf4j.Logger; |
---|
| 32 | import org.slf4j.LoggerFactory; |
---|
| 33 | |
---|
| 34 | import eu.clarin.sru.client.SRUClientException; |
---|
| 35 | import eu.clarin.sru.client.SRUExtraResponseData; |
---|
| 36 | import eu.clarin.sru.client.SRUExtraResponseDataParser; |
---|
| 37 | import eu.clarin.sru.client.XmlStreamReaderUtils; |
---|
| 38 | import eu.clarin.sru.client.fcs.ClarinFCSEndpointDescription.DataView; |
---|
| 39 | import eu.clarin.sru.client.fcs.ClarinFCSEndpointDescription.DataView.DeliveryPolicy; |
---|
[6917] | 40 | import eu.clarin.sru.client.fcs.ClarinFCSEndpointDescription.Layer; |
---|
| 41 | import eu.clarin.sru.client.fcs.ClarinFCSEndpointDescription.Layer.ContentEncoding; |
---|
[5798] | 42 | import eu.clarin.sru.client.fcs.ClarinFCSEndpointDescription.ResourceInfo; |
---|
| 43 | |
---|
| 44 | |
---|
| 45 | /** |
---|
| 46 | * An extra response data parser for parsing CLARIN-FCS endpoint descriptions. |
---|
| 47 | */ |
---|
| 48 | public class ClarinFCSEndpointDescriptionParser implements |
---|
| 49 | SRUExtraResponseDataParser { |
---|
[5804] | 50 | /** |
---|
| 51 | * constant for infinite resource enumeration parsing depth |
---|
| 52 | */ |
---|
[5798] | 53 | public static final int INFINITE_MAX_DEPTH = -1; |
---|
[5804] | 54 | /** |
---|
| 55 | * constant for default parsing resource enumeration parsing depth |
---|
| 56 | */ |
---|
[7073] | 57 | public static final int DEFAULT_MAX_DEPTH = INFINITE_MAX_DEPTH; |
---|
[5798] | 58 | private static final Logger logger = |
---|
| 59 | LoggerFactory.getLogger(ClarinFCSClientBuilder.class); |
---|
| 60 | private static final String ED_NS_URI = |
---|
| 61 | "http://clarin.eu/fcs/endpoint-description"; |
---|
| 62 | private static final QName ED_ROOT_ELEMENT = |
---|
| 63 | new QName(ED_NS_URI, "EndpointDescription"); |
---|
[6917] | 64 | private static final int VERSION_1 = 1; |
---|
| 65 | private static final int VERSION_2 = 2; |
---|
[5798] | 66 | private static final String CAPABILITY_PREFIX = |
---|
| 67 | "http://clarin.eu/fcs/capability/"; |
---|
| 68 | private static final String MIMETYPE_HITS_DATAVIEW = |
---|
| 69 | "application/x-clarin-fcs-hits+xml"; |
---|
| 70 | private final int maxDepth; |
---|
| 71 | |
---|
| 72 | |
---|
[5804] | 73 | /** |
---|
| 74 | * Constructor. By default, the parser will parse the endpoint resource |
---|
| 75 | * enumeration to an infinite depth. |
---|
| 76 | */ |
---|
[5798] | 77 | public ClarinFCSEndpointDescriptionParser() { |
---|
| 78 | this(DEFAULT_MAX_DEPTH); |
---|
| 79 | } |
---|
| 80 | |
---|
| 81 | |
---|
[5804] | 82 | /** |
---|
| 83 | * Constructor. |
---|
| 84 | * |
---|
| 85 | * @param maxDepth |
---|
| 86 | * maximum depth for parsing the endpoint resource enumeration. |
---|
| 87 | * @throws IllegalArgumentException |
---|
| 88 | * if an argument is illegal |
---|
| 89 | */ |
---|
[5798] | 90 | public ClarinFCSEndpointDescriptionParser(int maxDepth) { |
---|
| 91 | if (maxDepth < -1) { |
---|
| 92 | throw new IllegalArgumentException("maxDepth < -1"); |
---|
| 93 | } |
---|
| 94 | this.maxDepth = maxDepth; |
---|
| 95 | } |
---|
| 96 | |
---|
| 97 | |
---|
| 98 | @Override |
---|
| 99 | public boolean supports(QName name) { |
---|
| 100 | return ED_ROOT_ELEMENT.equals(name); |
---|
| 101 | } |
---|
| 102 | |
---|
| 103 | |
---|
| 104 | @Override |
---|
| 105 | public SRUExtraResponseData parse(XMLStreamReader reader) |
---|
| 106 | throws XMLStreamException, SRUClientException { |
---|
| 107 | final int version = parseVersion(reader); |
---|
[6917] | 108 | if ((version != VERSION_1) && (version != VERSION_2)) { |
---|
[5798] | 109 | throw new SRUClientException("Attribute 'version' of " + |
---|
[6917] | 110 | "element '<EndpointDescription>' must be of value '1' or '2'"); |
---|
[5798] | 111 | } |
---|
| 112 | reader.next(); // consume start tag |
---|
| 113 | |
---|
| 114 | // Capabilities |
---|
| 115 | List<URI> capabilities = null; |
---|
| 116 | XmlStreamReaderUtils.readStart(reader, ED_NS_URI, "Capabilities", true); |
---|
| 117 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 118 | "Capability", (capabilities == null))) { |
---|
| 119 | final String s = XmlStreamReaderUtils.readString(reader, true); |
---|
| 120 | try { |
---|
| 121 | if (!s.startsWith(CAPABILITY_PREFIX)) { |
---|
| 122 | throw new XMLStreamException("Capabilites must start " + |
---|
| 123 | "with prefix '" + CAPABILITY_PREFIX + |
---|
| 124 | "' (offending value = '" + s +"')", |
---|
| 125 | reader.getLocation()); |
---|
| 126 | } |
---|
| 127 | final URI uri = new URI(s); |
---|
| 128 | if (capabilities == null) { |
---|
[7280] | 129 | capabilities = new ArrayList<>(); |
---|
[5798] | 130 | } |
---|
| 131 | capabilities.add(uri); |
---|
[7200] | 132 | logger.debug("parsed capability:{}", uri); |
---|
[5798] | 133 | } catch (URISyntaxException e) { |
---|
| 134 | throw new XMLStreamException("Capabilities must be encoded " + |
---|
| 135 | "as URIs (offending value = '" + s + "')", |
---|
| 136 | reader.getLocation(), e); |
---|
| 137 | } |
---|
| 138 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Capability"); |
---|
| 139 | } // while |
---|
| 140 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Capabilities"); |
---|
[6917] | 141 | |
---|
| 142 | if (capabilities == null) { |
---|
| 143 | throw new SRUClientException("Endpoint must support at " + |
---|
| 144 | "least one capability!"); |
---|
| 145 | } |
---|
[7195] | 146 | final boolean hasBasicSearch = (capabilities |
---|
| 147 | .indexOf(ClarinFCSConstants.CAPABILITY_BASIC_SEARCH) != -1); |
---|
| 148 | final boolean hasAdvancedSearch = (capabilities |
---|
| 149 | .indexOf(ClarinFCSConstants.CAPABILITY_ADVANCED_SEARCH) != -1); |
---|
[6917] | 150 | if (!hasBasicSearch) { |
---|
[7195] | 151 | throw new SRUClientException( |
---|
| 152 | "Endpoint must support " + "'basic-search' (" + |
---|
| 153 | ClarinFCSConstants.CAPABILITY_BASIC_SEARCH + |
---|
| 154 | ") to conform to CLARIN-FCS specification"); |
---|
[5798] | 155 | } |
---|
| 156 | |
---|
| 157 | // SupportedDataViews |
---|
| 158 | List<DataView> supportedDataViews = null; |
---|
| 159 | XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 160 | "SupportedDataViews", true); |
---|
| 161 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 162 | "SupportedDataView", (supportedDataViews == null), true)) { |
---|
| 163 | final String id = XmlStreamReaderUtils.readAttributeValue( |
---|
| 164 | reader, null, "id", true); |
---|
| 165 | if ((id.indexOf(' ') != -1) || (id.indexOf(',') != -1) || |
---|
| 166 | (id.indexOf(';') != -1)) { |
---|
| 167 | throw new XMLStreamException("Value of attribute 'id' on " + |
---|
| 168 | "element '<SupportedDataView>' may not contain the " + |
---|
| 169 | "characters ',' (comma) or ';' (semicolon) " + |
---|
| 170 | "or ' ' (space)", reader.getLocation()); |
---|
| 171 | } |
---|
| 172 | final DeliveryPolicy policy = parsePolicy(reader); |
---|
| 173 | reader.next(); // consume start tag |
---|
| 174 | |
---|
| 175 | final String type = XmlStreamReaderUtils.readString(reader, true); |
---|
| 176 | // do some sanity checks ... |
---|
| 177 | if (supportedDataViews != null) { |
---|
| 178 | for (DataView dataView : supportedDataViews) { |
---|
| 179 | if (dataView.getIdentifier().equals(id)) { |
---|
| 180 | throw new XMLStreamException("Supported data view " + |
---|
| 181 | "with identifier '" + id + |
---|
| 182 | "' was already declared", reader.getLocation()); |
---|
| 183 | } |
---|
| 184 | if (dataView.getMimeType().equals(type)) { |
---|
| 185 | throw new XMLStreamException("Supported data view " + |
---|
| 186 | "with MIME type '" + type + |
---|
| 187 | "' was already declared", reader.getLocation()); |
---|
| 188 | } |
---|
| 189 | } |
---|
| 190 | } else { |
---|
[7280] | 191 | supportedDataViews = new ArrayList<>(); |
---|
[5798] | 192 | } |
---|
| 193 | supportedDataViews.add(new DataView(id, type, policy)); |
---|
| 194 | XmlStreamReaderUtils.readEnd(reader, |
---|
| 195 | ED_NS_URI, "SupportedDataView"); |
---|
| 196 | } // while |
---|
| 197 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, |
---|
| 198 | "SupportedDataViews", true); |
---|
| 199 | boolean found = false; |
---|
| 200 | if (supportedDataViews != null) { |
---|
| 201 | for (DataView dataView : supportedDataViews) { |
---|
| 202 | if (MIMETYPE_HITS_DATAVIEW.equals(dataView.getMimeType())) { |
---|
| 203 | found = true; |
---|
| 204 | break; |
---|
| 205 | } |
---|
| 206 | } |
---|
| 207 | } |
---|
| 208 | if (!found) { |
---|
| 209 | throw new SRUClientException("Endpoint must support " + |
---|
| 210 | "generic hits dataview (expected MIME type '" + |
---|
| 211 | MIMETYPE_HITS_DATAVIEW + |
---|
| 212 | "') to conform to CLARIN-FCS specification"); |
---|
| 213 | } |
---|
| 214 | |
---|
[6903] | 215 | // SupportedLayers |
---|
[6917] | 216 | List<Layer> supportedLayers = null; |
---|
[6903] | 217 | if (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 218 | "SupportedLayers", false)) { |
---|
[6917] | 219 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 220 | "SupportedLayer", (supportedLayers == null), true)) { |
---|
| 221 | final String id = XmlStreamReaderUtils.readAttributeValue(reader, null, "id"); |
---|
| 222 | if ((id.indexOf(' ') != -1) || (id.indexOf(',') != -1) || |
---|
| 223 | (id.indexOf(';') != -1)) { |
---|
| 224 | throw new XMLStreamException("Value of attribute 'id' on " + |
---|
| 225 | "element '<SupportedLayer>' may not contain the " + |
---|
| 226 | "characters ',' (comma) or ';' (semicolon) " + |
---|
| 227 | "or ' ' (space)", reader.getLocation()); |
---|
| 228 | } |
---|
| 229 | URI resultId = null; |
---|
| 230 | final String s1 = |
---|
| 231 | XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 232 | null, "result-id"); |
---|
| 233 | try { |
---|
| 234 | resultId = new URI(s1); |
---|
| 235 | } catch (URISyntaxException e) { |
---|
| 236 | throw new XMLStreamException("'result-id' must be encoded " + |
---|
| 237 | "as URIs (offending value = '" + s1 + "')", |
---|
| 238 | reader.getLocation(), e); |
---|
| 239 | } |
---|
| 240 | final ContentEncoding encoding = parseContentEncoding(reader); |
---|
| 241 | String qualifier = XmlStreamReaderUtils.readAttributeValue(reader, null, "qualifier", false); |
---|
| 242 | String altValueInfo = XmlStreamReaderUtils.readAttributeValue(reader, null, "alt-value-info", false); |
---|
| 243 | URI altValueInfoURI = null; |
---|
| 244 | final String s2 = |
---|
| 245 | XmlStreamReaderUtils.readAttributeValue(reader, null, |
---|
| 246 | "alt-value-info-uri", false); |
---|
| 247 | if (s2 != null) { |
---|
| 248 | try { |
---|
| 249 | altValueInfoURI = new URI(s2); |
---|
| 250 | } catch (URISyntaxException e) { |
---|
| 251 | throw new XMLStreamException("'alt-value-info-uri' must be encoded " + |
---|
| 252 | "as URIs (offending value = '" + s2 + "')", |
---|
| 253 | reader.getLocation(), e); |
---|
| 254 | } |
---|
| 255 | } |
---|
| 256 | reader.next(); // consume element |
---|
| 257 | final String layer = XmlStreamReaderUtils.readString(reader, true); |
---|
| 258 | logger.debug("layer: id={}, resultId={}, layer={}, encoding={}, qualifier={}, alt-value-info={}, alt-value-info-uri={}", |
---|
| 259 | id, resultId, layer, encoding, qualifier, altValueInfo, altValueInfoURI); |
---|
| 260 | |
---|
| 261 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "SupportedLayer"); |
---|
| 262 | if (supportedLayers == null) { |
---|
[7280] | 263 | supportedLayers = new ArrayList<>(); |
---|
[6917] | 264 | } |
---|
| 265 | supportedLayers.add(new Layer(id, resultId, layer, |
---|
| 266 | encoding, qualifier, altValueInfo, |
---|
| 267 | altValueInfoURI)); |
---|
| 268 | } // while |
---|
[6903] | 269 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, |
---|
| 270 | "SupportedLayers", true); |
---|
| 271 | } |
---|
[6917] | 272 | if (hasAdvancedSearch && (supportedLayers == null)) { |
---|
| 273 | throw new SRUClientException("Endpoint must declare " + |
---|
| 274 | "all supported layers (<SupportedLayers>) if they " + |
---|
| 275 | "provide the 'advanced-search' (" + |
---|
[7195] | 276 | ClarinFCSConstants.CAPABILITY_ADVANCED_SEARCH + |
---|
| 277 | ") capability"); |
---|
[6917] | 278 | } |
---|
| 279 | if (!hasAdvancedSearch && (supportedLayers != null)) { |
---|
| 280 | // XXX: hard error?! |
---|
| 281 | logger.warn("Endpoint superflously declared supported " + |
---|
| 282 | "layers (<SupportedLayers> without providing the " + |
---|
[7195] | 283 | "'advanced-search' (" + |
---|
| 284 | ClarinFCSConstants.CAPABILITY_ADVANCED_SEARCH + |
---|
| 285 | ") capability"); |
---|
[6917] | 286 | } |
---|
[6903] | 287 | |
---|
[5798] | 288 | // Resources |
---|
| 289 | final List<ResourceInfo> resources = |
---|
[7257] | 290 | parseResources(reader, 0, maxDepth, hasAdvancedSearch, |
---|
[6917] | 291 | supportedDataViews, supportedLayers); |
---|
[5798] | 292 | |
---|
| 293 | // skip over extensions |
---|
| 294 | while (!XmlStreamReaderUtils.peekEnd(reader, |
---|
| 295 | ED_NS_URI, "EndpointDescription")) { |
---|
| 296 | if (reader.isStartElement()) { |
---|
| 297 | final String namespaceURI = reader.getNamespaceURI(); |
---|
| 298 | final String localName = reader.getLocalName(); |
---|
| 299 | logger.debug("skipping over extension with element {{}}{}", |
---|
| 300 | namespaceURI, localName); |
---|
| 301 | XmlStreamReaderUtils.skipTag(reader, namespaceURI, localName); |
---|
| 302 | } |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "EndpointDescription"); |
---|
| 306 | |
---|
| 307 | return new ClarinFCSEndpointDescription(version, capabilities, |
---|
[6917] | 308 | supportedDataViews, supportedLayers, resources); |
---|
[5798] | 309 | } |
---|
| 310 | |
---|
| 311 | |
---|
[5804] | 312 | /** |
---|
| 313 | * Get the maximum resource enumeration parsing depth. The first level is |
---|
| 314 | * indicate by the value <code>0</code>. |
---|
| 315 | * |
---|
| 316 | * @return the default resource parsing depth or <code>-1</code> for |
---|
| 317 | * infinite. |
---|
| 318 | */ |
---|
| 319 | public int getMaximumResourcePArsingDepth() { |
---|
| 320 | return maxDepth; |
---|
| 321 | } |
---|
| 322 | |
---|
| 323 | |
---|
| 324 | private static List<ResourceInfo> parseResources(XMLStreamReader reader, |
---|
[7257] | 325 | int depth, int maxDepth, boolean hasAdvancedSearch, |
---|
| 326 | List<DataView> supportedDataviews, List<Layer> supportedLayers) |
---|
| 327 | throws XMLStreamException { |
---|
[5798] | 328 | List<ResourceInfo> resources = null; |
---|
| 329 | |
---|
| 330 | XmlStreamReaderUtils.readStart(reader, ED_NS_URI, "Resources", true); |
---|
| 331 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 332 | "Resource", (resources == null), true)) { |
---|
| 333 | final String pid = XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 334 | null, "pid", true); |
---|
| 335 | reader.next(); // consume start tag |
---|
| 336 | |
---|
[7257] | 337 | logger.debug("hasAdvSearch: {}", hasAdvancedSearch); |
---|
[7280] | 338 | |
---|
[7257] | 339 | logger.debug("parsing resource with pid = {}", pid); |
---|
[5798] | 340 | |
---|
| 341 | final Map<String, String> title = |
---|
| 342 | parseI18String(reader, "Title", true); |
---|
| 343 | logger.debug("title: {}", title); |
---|
| 344 | |
---|
| 345 | final Map<String, String> description = |
---|
| 346 | parseI18String(reader, "Description", false); |
---|
| 347 | logger.debug("description: {}", description); |
---|
| 348 | |
---|
| 349 | final String landingPageURI = |
---|
| 350 | XmlStreamReaderUtils.readContent(reader, ED_NS_URI, |
---|
| 351 | "LandingPageURI", false); |
---|
| 352 | logger.debug("landingPageURI: {}", landingPageURI); |
---|
| 353 | |
---|
| 354 | List<String> languages = null; |
---|
| 355 | XmlStreamReaderUtils.readStart(reader, |
---|
| 356 | ED_NS_URI, "Languages", true); |
---|
| 357 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 358 | "Language", (languages == null))) { |
---|
| 359 | final String language = |
---|
| 360 | XmlStreamReaderUtils.readString(reader, true); |
---|
| 361 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Language"); |
---|
| 362 | if (languages == null) { |
---|
[7280] | 363 | languages = new ArrayList<>(); |
---|
[5798] | 364 | } else { |
---|
| 365 | for (String l : languages) { |
---|
| 366 | if (l.equals(language)) { |
---|
| 367 | throw new XMLStreamException("language '" + |
---|
| 368 | language + "' was already defined " + |
---|
| 369 | "in '<Language>'", reader.getLocation()); |
---|
| 370 | } |
---|
| 371 | } // for |
---|
| 372 | } |
---|
| 373 | languages.add(language); |
---|
| 374 | } // while |
---|
| 375 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Languages", true); |
---|
| 376 | logger.debug("languages: {}", languages); |
---|
| 377 | |
---|
[6917] | 378 | // AvailableDataViews |
---|
[5798] | 379 | XmlStreamReaderUtils.readStart(reader, ED_NS_URI, "AvailableDataViews", true, true); |
---|
| 380 | final String dvs = XmlStreamReaderUtils.readAttributeValue(reader, null, "ref", true); |
---|
| 381 | reader.next(); // consume start tag |
---|
| 382 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "AvailableDataViews"); |
---|
| 383 | List<DataView> dataviews = null; |
---|
| 384 | for (String dv : dvs.split("\\s+")) { |
---|
| 385 | boolean found = false; |
---|
[7257] | 386 | if (supportedDataviews != null) { |
---|
| 387 | for (DataView dataview : supportedDataviews) { |
---|
| 388 | if (dataview.getIdentifier().equals(dv)) { |
---|
| 389 | found = true; |
---|
| 390 | if (dataviews == null) { |
---|
[7280] | 391 | dataviews = new ArrayList<>(); |
---|
[7257] | 392 | } |
---|
| 393 | dataviews.add(dataview); |
---|
| 394 | break; |
---|
[5798] | 395 | } |
---|
[7257] | 396 | } // for |
---|
| 397 | } |
---|
[5798] | 398 | if (!found) { |
---|
| 399 | throw new XMLStreamException("DataView with id '" + dv + |
---|
| 400 | "' was not declared in <SupportedDataViews>", |
---|
| 401 | reader.getLocation()); |
---|
| 402 | } |
---|
| 403 | } // for |
---|
| 404 | logger.debug("DataViews: {}", dataviews); |
---|
| 405 | |
---|
[6917] | 406 | // AvailableLayers |
---|
| 407 | List<Layer> layers = null; |
---|
| 408 | if (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, |
---|
| 409 | "AvailableLayers", false, true)) { |
---|
| 410 | final String ls = |
---|
| 411 | XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 412 | null, "ref", true); |
---|
[6903] | 413 | reader.next(); // consume start tag |
---|
| 414 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "AvailableLayers"); |
---|
[6917] | 415 | for (String l : ls.split("\\s+")) { |
---|
| 416 | boolean found = false; |
---|
[7257] | 417 | if (supportedLayers != null) { |
---|
| 418 | for (Layer layer : supportedLayers) { |
---|
| 419 | if (layer.getIdentifier().equals(l)) { |
---|
| 420 | found = true; |
---|
| 421 | if (layers == null) { |
---|
[7280] | 422 | layers = new ArrayList<>(); |
---|
[7257] | 423 | } |
---|
| 424 | layers.add(layer); |
---|
| 425 | break; |
---|
[6917] | 426 | } |
---|
[7257] | 427 | } // for |
---|
| 428 | } |
---|
[6917] | 429 | if (!found) { |
---|
| 430 | throw new XMLStreamException("Layer with id '" + l + |
---|
| 431 | "' was not declared in <SupportedLayers>", |
---|
| 432 | reader.getLocation()); |
---|
| 433 | } |
---|
| 434 | } // for |
---|
| 435 | logger.debug("Layers: {}", layers); |
---|
[7257] | 436 | } // for |
---|
| 437 | if (hasAdvancedSearch && (layers == null)) { |
---|
| 438 | throw new XMLStreamException("Endpoint must declare " + |
---|
| 439 | "all available layers (<AvailableLayers>) on a " + |
---|
| 440 | "resource, if they provide the 'advanced-search' (" + |
---|
| 441 | ClarinFCSConstants.CAPABILITY_ADVANCED_SEARCH + |
---|
| 442 | ") capability. Offending resource id pid=" + pid + |
---|
| 443 | ")", reader.getLocation()); |
---|
[6903] | 444 | } |
---|
| 445 | |
---|
| 446 | |
---|
[5798] | 447 | List<ResourceInfo> subResources = null; |
---|
[5804] | 448 | if (XmlStreamReaderUtils.peekStart(reader, |
---|
| 449 | ED_NS_URI, "Resources")) { |
---|
[5798] | 450 | final int nextDepth = depth + 1; |
---|
[5804] | 451 | if ((maxDepth == INFINITE_MAX_DEPTH) || |
---|
| 452 | (nextDepth < maxDepth)) { |
---|
[5798] | 453 | subResources = parseResources(reader, nextDepth, |
---|
[7257] | 454 | maxDepth, hasAdvancedSearch, supportedDataviews, |
---|
| 455 | supportedLayers); |
---|
[5798] | 456 | } else { |
---|
| 457 | XmlStreamReaderUtils.skipTag(reader, ED_NS_URI, |
---|
| 458 | "Resources", true); |
---|
| 459 | } |
---|
| 460 | } |
---|
| 461 | |
---|
| 462 | while (!XmlStreamReaderUtils.peekEnd(reader, |
---|
| 463 | ED_NS_URI, "Resource")) { |
---|
| 464 | if (reader.isStartElement()) { |
---|
| 465 | final String namespaceURI = reader.getNamespaceURI(); |
---|
| 466 | final String localName = reader.getLocalName(); |
---|
| 467 | logger.debug("skipping over extension with element " + |
---|
| 468 | "{{}}{} (resource)", namespaceURI, localName); |
---|
| 469 | XmlStreamReaderUtils.skipTag(reader, |
---|
| 470 | namespaceURI, localName); |
---|
| 471 | } |
---|
| 472 | } // while |
---|
| 473 | |
---|
| 474 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Resource"); |
---|
| 475 | |
---|
| 476 | if (resources == null) { |
---|
[7280] | 477 | resources = new ArrayList<>(); |
---|
[5798] | 478 | } |
---|
| 479 | resources.add(new ResourceInfo(pid, title, description, |
---|
[6917] | 480 | landingPageURI, languages, dataviews, layers, |
---|
| 481 | subResources)); |
---|
[5798] | 482 | } // while |
---|
| 483 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, "Resources"); |
---|
| 484 | |
---|
| 485 | return resources; |
---|
| 486 | } |
---|
| 487 | |
---|
| 488 | |
---|
| 489 | private static Map<String, String> parseI18String(XMLStreamReader reader, |
---|
| 490 | String localName, boolean required) throws XMLStreamException { |
---|
| 491 | Map<String, String> result = null; |
---|
| 492 | while (XmlStreamReaderUtils.readStart(reader, ED_NS_URI, localName, |
---|
| 493 | ((result == null) && required), true)) { |
---|
| 494 | final String lang = XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 495 | XMLConstants.XML_NS_URI, "lang", true); |
---|
| 496 | reader.next(); // skip start tag |
---|
| 497 | final String content = XmlStreamReaderUtils.readString(reader, true); |
---|
| 498 | if (result == null) { |
---|
[7280] | 499 | result = new HashMap<>(); |
---|
[5798] | 500 | } |
---|
| 501 | if (result.containsKey(lang)) { |
---|
| 502 | throw new XMLStreamException("language '" + lang + |
---|
| 503 | "' already defined for element '<" + localName + ">'", |
---|
| 504 | reader.getLocation()); |
---|
| 505 | } else { |
---|
| 506 | result.put(lang, content); |
---|
| 507 | } |
---|
| 508 | XmlStreamReaderUtils.readEnd(reader, ED_NS_URI, localName); |
---|
| 509 | } // while |
---|
| 510 | return result; |
---|
| 511 | } |
---|
| 512 | |
---|
| 513 | |
---|
| 514 | private static int parseVersion(XMLStreamReader reader) |
---|
| 515 | throws XMLStreamException { |
---|
| 516 | try { |
---|
| 517 | final String s = XmlStreamReaderUtils.readAttributeValue( |
---|
| 518 | reader, null, "version", true); |
---|
| 519 | return Integer.parseInt(s); |
---|
| 520 | } catch (NumberFormatException e) { |
---|
| 521 | throw new XMLStreamException("Attribute 'version' is not a number", |
---|
| 522 | reader.getLocation(), e); |
---|
| 523 | } |
---|
| 524 | } |
---|
| 525 | |
---|
| 526 | |
---|
| 527 | private static DeliveryPolicy parsePolicy(XMLStreamReader reader) |
---|
| 528 | throws XMLStreamException { |
---|
| 529 | final String s = XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 530 | null, "delivery-policy", true); |
---|
| 531 | if ("send-by-default".equals(s)) { |
---|
| 532 | return DeliveryPolicy.SEND_BY_DEFAULT; |
---|
| 533 | } else if ("need-to-request".equals(s)) { |
---|
| 534 | return DeliveryPolicy.NEED_TO_REQUEST; |
---|
| 535 | } else { |
---|
| 536 | throw new XMLStreamException("Unexpected value '" + s + |
---|
| 537 | "' for attribute 'delivery-policy' on " + |
---|
| 538 | "element '<SupportedDataView>'", reader.getLocation()); |
---|
| 539 | } |
---|
| 540 | } |
---|
| 541 | |
---|
[6917] | 542 | |
---|
| 543 | private static ContentEncoding parseContentEncoding(XMLStreamReader reader) |
---|
| 544 | throws XMLStreamException { |
---|
| 545 | final String s = XmlStreamReaderUtils.readAttributeValue(reader, |
---|
| 546 | null, "encoding", false); |
---|
| 547 | if (s != null) { |
---|
| 548 | if ("value".equals(s)) { |
---|
| 549 | return ContentEncoding.VALUE; |
---|
| 550 | } else if ("need-to-request".equals(s)) { |
---|
| 551 | return ContentEncoding.EMPTY; |
---|
| 552 | } else { |
---|
| 553 | throw new XMLStreamException("Unexpected value '" + s + |
---|
| 554 | "' for attribute 'encoding' on " + |
---|
| 555 | "element '<SupportedLayer>'", |
---|
| 556 | reader.getLocation()); |
---|
| 557 | } |
---|
| 558 | } else { |
---|
| 559 | return null; |
---|
| 560 | } |
---|
| 561 | } |
---|
| 562 | |
---|
[5798] | 563 | } // class ClarinFCSEndpointDescriptionParser |
---|