Changeset 10993 in josm for trunk


Ignore:
Timestamp:
2016-09-11T12:13:06+02:00 (3 years ago)
Author:
wiktorn
Message:

WMTS TileSource refactor

  • introduced GetCapabilitiesParseHelper, that can be reused for WMS
  • replaced all dynamix QNames with static ones
  • added CacheFile.clear() method
  • clearing CacheFile on WMTS GetCapabilities parse error
Location:
trunk/src/org/openstreetmap/josm
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java

    r10680 r10993  
    99import java.io.IOException;
    1010import java.io.InputStream;
    11 import java.net.MalformedURLException;
    12 import java.net.URL;
    1311import java.util.ArrayList;
    1412import java.util.Collection;
     
    3129import javax.swing.table.AbstractTableModel;
    3230import javax.xml.namespace.QName;
    33 import javax.xml.stream.XMLInputFactory;
    3431import javax.xml.stream.XMLStreamException;
    3532import javax.xml.stream.XMLStreamReader;
     
    6057 */
    6158public class WMTSTileSource extends AbstractTMSTileSource implements TemplatedTileSource {
     59    /**
     60     * WMTS namespace address
     61     */
     62    public static final String WMTS_NS_URL = "http://www.opengis.net/wmts/1.0";
     63
     64    // CHECKSTYLE.OFF: SingleSpaceSeparator
     65    private static final QName QN_CONTENTS            = new QName(WMTSTileSource.WMTS_NS_URL, "Contents");
     66    private static final QName QN_FORMAT              = new QName(WMTSTileSource.WMTS_NS_URL, "Format");
     67    private static final QName QN_LAYER               = new QName(WMTSTileSource.WMTS_NS_URL, "Layer");
     68    private static final QName QN_MATRIX_WIDTH        = new QName(WMTSTileSource.WMTS_NS_URL, "MatrixWidth");
     69    private static final QName QN_MATRIX_HEIGHT       = new QName(WMTSTileSource.WMTS_NS_URL, "MatrixHeight");
     70    private static final QName QN_RESOURCE_URL        = new QName(WMTSTileSource.WMTS_NS_URL, "ResourceURL");
     71    private static final QName QN_SCALE_DENOMINATOR   = new QName(WMTSTileSource.WMTS_NS_URL, "ScaleDenominator");
     72    private static final QName QN_STYLE               = new QName(WMTSTileSource.WMTS_NS_URL, "Style");
     73    private static final QName QN_TILEMATRIX          = new QName(WMTSTileSource.WMTS_NS_URL, "TileMatrix");
     74    private static final QName QN_TILEMATRIXSET       = new QName(WMTSTileSource.WMTS_NS_URL, "TileMatrixSet");
     75    private static final QName QN_TILEMATRIX_SET_LINK = new QName(WMTSTileSource.WMTS_NS_URL, "TileMatrixSetLink");
     76    private static final QName QN_TILE_WIDTH          = new QName(WMTSTileSource.WMTS_NS_URL, "TileWidth");
     77    private static final QName QN_TILE_HEIGHT         = new QName(WMTSTileSource.WMTS_NS_URL, "TileHeight");
     78    private static final QName QN_TOPLEFT_CORNER      = new QName(WMTSTileSource.WMTS_NS_URL, "TopLeftCorner");
     79    // CHECKSTYLE.ON: SingleSpaceSeparator
     80
    6281    private static final String PATTERN_HEADER = "\\{header\\(([^,]+),([^}]+)\\)\\}";
    6382
     
    6887        PATTERN_HEADER,
    6988    };
    70 
    71     private static final String OWS_NS_URL = "http://www.opengis.net/ows/1.1";
    72     private static final String WMTS_NS_URL = "http://www.opengis.net/wmts/1.0";
    73     private static final String XLINK_NS_URL = "http://www.w3.org/1999/xlink";
    7489
    7590    private static class TileMatrix {
     
    139154
    140155        Layer() {
    141         }
    142     }
    143 
    144     private enum TransferMode {
    145         KVP("KVP"),
    146         REST("RESTful");
    147 
    148         private final String typeString;
    149 
    150         TransferMode(String urlString) {
    151             this.typeString = urlString;
    152         }
    153 
    154         private String getTypeString() {
    155             return typeString;
    156         }
    157 
    158         private static TransferMode fromString(String s) {
    159             for (TransferMode type : TransferMode.values()) {
    160                 if (type.getTypeString().equals(s)) {
    161                     return type;
    162                 }
    163             }
    164             return null;
    165156        }
    166157    }
     
    238229    private TileMatrixSet currentTileMatrixSet;
    239230    private double crsScale;
    240     private TransferMode transferMode;
     231    private GetCapabilitiesParseHelper.TransferMode transferMode;
    241232
    242233    private ScaleList nativeScaleList;
     
    250241    public WMTSTileSource(ImageryInfo info) throws IOException {
    251242        super(info);
    252         this.baseUrl = normalizeCapabilitiesUrl(handleTemplate(info.getUrl()));
     243        this.baseUrl = GetCapabilitiesParseHelper.normalizeCapabilitiesUrl(handleTemplate(info.getUrl()));
    253244        this.layers = getCapabilities();
    254245        if (this.layers.isEmpty())
     
    291282     */
    292283    private Collection<Layer> getCapabilities() throws IOException {
    293         XMLInputFactory factory = XMLInputFactory.newFactory();
    294         // do not try to load external entities, nor validate the XML
    295         factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
    296         factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
    297         factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
    298 
    299284        try (CachedFile cf = new CachedFile(baseUrl); InputStream in = cf.setHttpHeaders(headers).
    300285                setMaxAge(7 * CachedFile.DAYS).
     
    303288            byte[] data = Utils.readBytesFromStream(in);
    304289            if (data == null || data.length == 0) {
     290                cf.clear();
    305291                throw new IllegalArgumentException("Could not read data from: " + baseUrl);
    306292            }
    307             XMLStreamReader reader = factory.createXMLStreamReader(new ByteArrayInputStream(data));
    308 
    309             Collection<Layer> ret = new ArrayList<>();
    310             for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) {
    311                 if (event == XMLStreamReader.START_ELEMENT) {
    312                     if (new QName(OWS_NS_URL, "OperationsMetadata").equals(reader.getName())) {
    313                         parseOperationMetadata(reader);
     293
     294            try {
     295                XMLStreamReader reader = GetCapabilitiesParseHelper.getReader(new ByteArrayInputStream(data));
     296                Collection<Layer> ret = new ArrayList<>();
     297                for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) {
     298                    if (event == XMLStreamReader.START_ELEMENT) {
     299                        if (GetCapabilitiesParseHelper.QN_OWS_OPERATIONS_METADATA.equals(reader.getName())) {
     300                            parseOperationMetadata(reader);
     301                        }
     302
     303                        if (QN_CONTENTS.equals(reader.getName())) {
     304                            ret = parseContents(reader);
     305                        }
    314306                    }
    315 
    316                     if (new QName(WMTS_NS_URL, "Contents").equals(reader.getName())) {
    317                         ret = parseContents(reader);
    318                     }
    319                 }
    320             }
    321             return ret;
    322         } catch (XMLStreamException e) {
    323             throw new IllegalArgumentException(e);
    324         }
    325     }
    326 
    327     /**
    328      * Parse Contents tag. Renturns when reader reaches Contents closing tag
     307                }
     308                return ret;
     309            } catch (XMLStreamException e) {
     310                cf.clear();
     311                throw new IllegalArgumentException(e);
     312            }
     313        }
     314    }
     315
     316    /**
     317     * Parse Contents tag. Returns when reader reaches Contents closing tag
    329318     *
    330319     * @param reader StAX reader instance
     
    336325        Collection<Layer> layers = new ArrayList<>();
    337326        for (int event = reader.getEventType();
    338                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && new QName(WMTS_NS_URL, "Contents").equals(reader.getName()));
     327                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && QN_CONTENTS.equals(reader.getName()));
    339328                event = reader.next()) {
    340329            if (event == XMLStreamReader.START_ELEMENT) {
    341                 if (new QName(WMTS_NS_URL, "Layer").equals(reader.getName())) {
     330                if (QN_LAYER.equals(reader.getName())) {
    342331                    layers.add(parseLayer(reader));
    343332                }
    344                 if (new QName(WMTS_NS_URL, "TileMatrixSet").equals(reader.getName())) {
     333                if (QN_TILEMATRIXSET.equals(reader.getName())) {
    345334                    TileMatrixSet entry = parseTileMatrixSet(reader);
    346335                    matrixSetById.put(entry.identifier, entry);
     
    372361
    373362        for (int event = reader.getEventType();
    374                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && new QName(WMTS_NS_URL, "Layer").equals(reader.getName()));
     363                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && QN_LAYER.equals(reader.getName()));
    375364                event = reader.next()) {
    376365            if (event == XMLStreamReader.START_ELEMENT) {
    377366                tagStack.push(reader.getName());
    378367                if (tagStack.size() == 2) {
    379                     if (new QName(WMTS_NS_URL, "Format").equals(reader.getName())) {
     368                    if (QN_FORMAT.equals(reader.getName())) {
    380369                        layer.format = reader.getElementText();
    381                     } else if (new QName(OWS_NS_URL, "Identifier").equals(reader.getName())) {
     370                    } else if (GetCapabilitiesParseHelper.QN_OWS_IDENTIFIER.equals(reader.getName())) {
    382371                        layer.name = reader.getElementText();
    383                     } else if (new QName(WMTS_NS_URL, "ResourceURL").equals(reader.getName()) &&
     372                    } else if (QN_RESOURCE_URL.equals(reader.getName()) &&
    384373                            "tile".equals(reader.getAttributeValue("", "resourceType"))) {
    385374                        layer.baseUrl = reader.getAttributeValue("", "template");
    386                     } else if (new QName(WMTS_NS_URL, "Style").equals(reader.getName()) &&
     375                    } else if (QN_STYLE.equals(reader.getName()) &&
    387376                            "true".equals(reader.getAttributeValue("", "isDefault"))) {
    388                         if (moveReaderToTag(reader, new QName[] {new QName(OWS_NS_URL, "Identifier")})) {
     377                        if (GetCapabilitiesParseHelper.moveReaderToTag(reader, new QName[] {GetCapabilitiesParseHelper.QN_OWS_IDENTIFIER})) {
    389378                            layer.style = reader.getElementText();
    390379                            tagStack.push(reader.getName()); // keep tagStack in sync
    391380                        }
    392                     } else if (new QName(WMTS_NS_URL, "TileMatrixSetLink").equals(reader.getName())) {
     381                    } else if (QN_TILEMATRIX_SET_LINK.equals(reader.getName())) {
    393382                        layer.tileMatrixSetLinks.add(praseTileMatrixSetLink(reader));
    394383                    } else {
    395                         moveReaderToEndCurrentTag(reader);
     384                        GetCapabilitiesParseHelper.moveReaderToEndCurrentTag(reader);
    396385                    }
    397386                }
     
    413402
    414403    /**
    415      * Moves the reader to the closing tag of current tag.
    416      * @param reader XML stream reader positioned on XMLStreamReader.START_ELEMENT
    417      * @throws XMLStreamException when parse exception occurs
    418      */
    419     private static void moveReaderToEndCurrentTag(XMLStreamReader reader) throws XMLStreamException {
    420         int level = 0;
    421         QName tag = reader.getName();
    422         for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) {
    423             switch (event) {
    424             case XMLStreamReader.START_ELEMENT:
    425                 level += 1;
    426                 break;
    427             case XMLStreamReader.END_ELEMENT:
    428                 level -= 1;
    429                 if (level == 0 && tag.equals(reader.getName())) {
    430                     return;
    431                 }
    432             }
    433             if (level < 0) {
    434                 throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag");
    435             }
    436         }
    437         throw new IllegalStateException("WMTS Parser error - moveReaderToEndCurrentTag failed to find closing tag");
    438 
    439     }
    440 
    441     /**
    442404     * Gets TileMatrixSetLink value. Returns when reader is on TileMatrixSetLink closing tag
    443405     *
     
    450412        for (int event = reader.getEventType();
    451413                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT &&
    452                         new QName(WMTS_NS_URL, "TileMatrixSetLink").equals(reader.getName()));
     414                        QN_TILEMATRIX_SET_LINK.equals(reader.getName()));
    453415                event = reader.next()) {
    454             if (event == XMLStreamReader.START_ELEMENT && new QName(WMTS_NS_URL, "TileMatrixSet").equals(reader.getName())) {
     416            if (event == XMLStreamReader.START_ELEMENT && QN_TILEMATRIXSET.equals(reader.getName())) {
    455417                ret = reader.getElementText();
    456418            }
     
    468430        TileMatrixSetBuilder matrixSet = new TileMatrixSetBuilder();
    469431        for (int event = reader.getEventType();
    470                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && new QName(WMTS_NS_URL, "TileMatrixSet").equals(reader.getName()));
     432                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && QN_TILEMATRIXSET.equals(reader.getName()));
    471433                event = reader.next()) {
    472434                    if (event == XMLStreamReader.START_ELEMENT) {
    473                         if (new QName(OWS_NS_URL, "Identifier").equals(reader.getName())) {
     435                        if (GetCapabilitiesParseHelper.QN_OWS_IDENTIFIER.equals(reader.getName())) {
    474436                            matrixSet.identifier = reader.getElementText();
    475437                        }
    476                         if (new QName(OWS_NS_URL, "SupportedCRS").equals(reader.getName())) {
    477                             matrixSet.crs = crsToCode(reader.getElementText());
    478                         }
    479                         if (new QName(WMTS_NS_URL, "TileMatrix").equals(reader.getName())) {
     438                        if (GetCapabilitiesParseHelper.QN_OWS_SUPPORTED_CRS.equals(reader.getName())) {
     439                            matrixSet.crs = GetCapabilitiesParseHelper.crsToCode(reader.getElementText());
     440                        }
     441                        if (QN_TILEMATRIX.equals(reader.getName())) {
    480442                            matrixSet.tileMatrix.add(parseTileMatrix(reader, matrixSet.crs));
    481443                        }
     
    501463        }
    502464        for (int event = reader.getEventType();
    503                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && new QName(WMTS_NS_URL, "TileMatrix").equals(reader.getName()));
     465                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && QN_TILEMATRIX.equals(reader.getName()));
    504466                event = reader.next()) {
    505467            if (event == XMLStreamReader.START_ELEMENT) {
    506                 if (new QName(OWS_NS_URL, "Identifier").equals(reader.getName())) {
     468                if (GetCapabilitiesParseHelper.QN_OWS_IDENTIFIER.equals(reader.getName())) {
    507469                    ret.identifier = reader.getElementText();
    508470                }
    509                 if (new QName(WMTS_NS_URL, "ScaleDenominator").equals(reader.getName())) {
     471                if (QN_SCALE_DENOMINATOR.equals(reader.getName())) {
    510472                    ret.scaleDenominator = Double.parseDouble(reader.getElementText());
    511473                }
    512                 if (new QName(WMTS_NS_URL, "TopLeftCorner").equals(reader.getName())) {
     474                if (QN_TOPLEFT_CORNER.equals(reader.getName())) {
    513475                    String[] topLeftCorner = reader.getElementText().split(" ");
    514476                    if (matrixProj.switchXY()) {
     
    518480                    }
    519481                }
    520                 if (new QName(WMTS_NS_URL, "TileHeight").equals(reader.getName())) {
     482                if (QN_TILE_HEIGHT.equals(reader.getName())) {
    521483                    ret.tileHeight = Integer.parseInt(reader.getElementText());
    522484                }
    523                 if (new QName(WMTS_NS_URL, "TileWidth").equals(reader.getName())) {
     485                if (QN_TILE_WIDTH.equals(reader.getName())) {
    524486                    ret.tileWidth = Integer.parseInt(reader.getElementText());
    525487                }
    526                 if (new QName(WMTS_NS_URL, "MatrixHeight").equals(reader.getName())) {
     488                if (QN_MATRIX_HEIGHT.equals(reader.getName())) {
    527489                    ret.matrixHeight = Integer.parseInt(reader.getElementText());
    528490                }
    529                 if (new QName(WMTS_NS_URL, "MatrixWidth").equals(reader.getName())) {
     491                if (QN_MATRIX_WIDTH.equals(reader.getName())) {
    530492                    ret.matrixWidth = Integer.parseInt(reader.getElementText());
    531493                }
     
    549511        for (int event = reader.getEventType();
    550512                reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT &&
    551                         new QName(OWS_NS_URL, "OperationsMetadata").equals(reader.getName()));
     513                        GetCapabilitiesParseHelper.QN_OWS_OPERATIONS_METADATA.equals(reader.getName()));
    552514                event = reader.next()) {
    553515            if (event == XMLStreamReader.START_ELEMENT) {
    554                 if (new QName(OWS_NS_URL, "Operation").equals(reader.getName()) && "GetTile".equals(reader.getAttributeValue("", "name")) &&
    555                         moveReaderToTag(reader, new QName[]{
    556                                 new QName(OWS_NS_URL, "DCP"),
    557                                 new QName(OWS_NS_URL, "HTTP"),
    558                                 new QName(OWS_NS_URL, "Get"),
     516                if (GetCapabilitiesParseHelper.QN_OWS_OPERATION.equals(reader.getName()) && "GetTile".equals(reader.getAttributeValue("", "name")) &&
     517                        GetCapabilitiesParseHelper.moveReaderToTag(reader, new QName[]{
     518                                GetCapabilitiesParseHelper.QN_OWS_DCP,
     519                                GetCapabilitiesParseHelper.QN_OWS_HTTP,
     520                                GetCapabilitiesParseHelper.QN_OWS_GET,
    559521
    560522                        })) {
    561                     this.baseUrl = reader.getAttributeValue(XLINK_NS_URL, "href");
    562                     this.transferMode = getTransferMode(reader);
    563                 }
    564             }
    565         }
    566     }
    567 
    568     /**
    569      * Parses Operation[@name='GetTile']/DCP/HTTP/Get section. Returns when reader is on Get closing tag.
    570      * @param reader StAX reader instance
    571      * @return TransferMode coded in this section
    572      * @throws XMLStreamException See {@link XMLStreamReader}
    573      */
    574     private static TransferMode getTransferMode(XMLStreamReader reader) throws XMLStreamException {
    575         QName getQname = new QName(OWS_NS_URL, "Get");
    576 
    577         Utils.ensure(getQname.equals(reader.getName()), "WMTS Parser state invalid. Expected element %s, got %s",
    578                 getQname, reader.getName());
    579         for (int event = reader.getEventType();
    580                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && getQname.equals(reader.getName()));
    581                 event = reader.next()) {
    582             if (event == XMLStreamReader.START_ELEMENT && new QName(OWS_NS_URL, "Constraint").equals(reader.getName())
    583              && "GetEncoding".equals(reader.getAttributeValue("", "name"))) {
    584                 moveReaderToTag(reader, new QName[]{
    585                         new QName(OWS_NS_URL, "AllowedValues"),
    586                         new QName(OWS_NS_URL, "Value")
    587                 });
    588                 return TransferMode.fromString(reader.getElementText());
    589             }
    590         }
    591         return null;
    592     }
    593 
    594     /**
    595      * Moves reader to first occurrence of the structure equivalent of Xpath tags[0]/tags[1]../tags[n]. If fails to find
    596      * moves the reader to the closing tag of current tag
    597      *
    598      * @param reader StAX reader instance
    599      * @param tags array of tags
    600      * @return true if tag was found, false otherwise
    601      * @throws XMLStreamException See {@link XMLStreamReader}
    602      */
    603     private static boolean moveReaderToTag(XMLStreamReader reader, QName ... tags) throws XMLStreamException {
    604         QName stopTag = reader.getName();
    605         int currentLevel = 0;
    606         QName searchTag = tags[currentLevel];
    607         QName parentTag = null;
    608         QName skipTag = null;
    609 
    610         for (int event = 0; //skip current element, so we will not skip it as a whole
    611                 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && stopTag.equals(reader.getName()));
    612                 event = reader.next()) {
    613             if (event == XMLStreamReader.END_ELEMENT && skipTag != null && skipTag.equals(reader.getName())) {
    614                 skipTag = null;
    615             }
    616             if (skipTag == null) {
    617                 if (event == XMLStreamReader.START_ELEMENT) {
    618                     if (searchTag.equals(reader.getName())) {
    619                         currentLevel += 1;
    620                         if (currentLevel >= tags.length) {
    621                             return true; // found!
    622                         }
    623                         parentTag = searchTag;
    624                         searchTag = tags[currentLevel];
    625                     } else {
    626                         skipTag = reader.getName();
    627                     }
    628                 }
    629 
    630                 if (event == XMLStreamReader.END_ELEMENT && parentTag != null && parentTag.equals(reader.getName())) {
    631                     currentLevel -= 1;
    632                     searchTag = parentTag;
    633                     if (currentLevel >= 0) {
    634                         parentTag = tags[currentLevel];
    635                     } else {
    636                         parentTag = null;
    637                     }
    638                 }
    639             }
    640         }
    641         return false;
    642     }
    643 
    644     private static String normalizeCapabilitiesUrl(String url) throws MalformedURLException {
    645         URL inUrl = new URL(url);
    646         URL ret = new URL(inUrl.getProtocol(), inUrl.getHost(), inUrl.getPort(), inUrl.getFile());
    647         return ret.toExternalForm();
    648     }
    649 
    650     private static String crsToCode(String crsIdentifier) {
    651         if (crsIdentifier.startsWith("urn:ogc:def:crs:")) {
    652             return crsIdentifier.replaceFirst("urn:ogc:def:crs:([^:]*):.*:(.*)$", "$1:$2");
    653         }
    654         return crsIdentifier;
     523                    this.baseUrl = reader.getAttributeValue(GetCapabilitiesParseHelper.XLINK_NS_URL, "href");
     524                    this.transferMode = GetCapabilitiesParseHelper.getTransferMode(reader);
     525                }
     526            }
     527        }
    655528    }
    656529
  • trunk/src/org/openstreetmap/josm/io/CachedFile.java

    r10686 r10993  
    525525        }
    526526    }
     527
     528    /**
     529     * Clears the cached file
     530     * @throws IOException
     531     * @since 10993
     532     */
     533    public void clear() throws IOException {
     534        File f = getFile();
     535        if (f != null && f.exists()) {
     536            Utils.deleteFile(f);
     537        }
     538    }
    527539}
Note: See TracChangeset for help on using the changeset viewer.