// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.io;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.tools.Utils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Represents the OSM API server capabilities.
*
* Example capabilites document:
* <pre>
* <osm version="0.6" generator="OpenStreetMap server">
* <api>
* <version minimum="0.6" maximum="0.6"/>
* <area maximum="0.25"/>
* <tracepoints per_page="5000"/>
* <waynodes maximum="2000"/>
* <changesets maximum_elements="10000"/>
* <timeout seconds="300"/>
* <status database="online" api="online" gpx="online"/>
* </api>
* <policy>
* <imagery>
* <blacklist regex=".*\.google\.com/.*"/>
* <blacklist regex=".*209\.85\.2\d\d.*"/>
* <blacklist regex=".*209\.85\.1[3-9]\d.*"/>
* <blacklist regex=".*209\.85\.12[89].*"/>
* </imagery>
* </policy>
* </osm>
* </pre>
* This class is used in conjunction with a very primitive parser
* and simply stuffs the each tag and its attributes into a hash
* of hashes, with the exception of the "blacklist" tag which gets
* a list of its own. The DOM hierarchy is disregarded.
*/
public class Capabilities {
private final Map<String, Map<String, String>> capabilities;
private final List<String> imageryBlacklist;
/**
* Constructs new {@code Capabilities}.
*/
public Capabilities() {
capabilities = new HashMap<>();
imageryBlacklist = new ArrayList<>();
}
/**
* Determines if given element and attribute are defined.
*
* @param element the name of the element
* @param attribute the name of the attribute
* @return {@code true} if defined, {@code false} otherwise
*/
public boolean isDefined(String element, String attribute) {
if (!capabilities.containsKey(element)) return false;
Map<String, String> e = capabilities.get(element);
if (e == null) return false;
return e.get(attribute) != null;
}
/**
* Returns the value of configuration item in the capabilities as string value.
*
* @param element the name of the element
* @param attribute the name of the attribute
* @return the value; {@code null}, if the respective configuration item does not exist
*/
public String get(String element, String attribute) {
if (!capabilities.containsKey(element)) return null;
Map<String, String> e = capabilities.get(element);
if (e == null) return null;
return e.get(attribute);
}
/**
* Returns the value of configuration item in the capabilities as double value.
*
* @param element the name of the element
* @param attribute the name of the attribute
* @return the value; {@code null}, if the respective configuration item does not exist
* @throws NumberFormatException if the value is not a valid double
*/
public Double getDouble(String element, String attribute) {
String s = get(element, attribute);
if (s == null) return null;
return Double.valueOf(s);
}
/**
* Returns the value of configuration item in the capabilities as long value.
*
* @param element the name of the element
* @param attribute the name of the attribute
* @return the value; {@code null}, if the respective configuration item does not exist
* @throws NumberFormatException if the value is not a valid long
*/
public Long getLong(String element, String attribute) {
String s = get(element, attribute);
if (s == null) return null;
return Long.valueOf(s);
}
/**
* Adds a new configuration item.
*
* @param element the name of the element
* @param attribute the name of the attribute
* @param value the value as string
*/
public void put(String element, String attribute, String value) {
if ("blacklist".equals(element)) {
if ("regex".equals(attribute)) {
imageryBlacklist.add(value);
}
} else {
if (!capabilities.containsKey(element)) {
capabilities.put(element, new HashMap<>());
}
capabilities.get(element).put(attribute, value);
}
}
/**
* Clears the API capabilities.
*/
public final void clear() {
capabilities.clear();
imageryBlacklist.clear();
}
/**
* Determines if a given API version is supported.
* @param version The API version to check
* @return {@code true} is version is between the minimum supported version and the maximum one, {@code false} otherwise
*/
public boolean supportsVersion(String version) {
return get("version", "minimum").compareTo(version) <= 0
&& get("version", "maximum").compareTo(version) >= 0;
}
private static void warnIllegalValue(String attr, String elem, Object val) {
Main.warn(tr("Illegal value of attribute ''{0}'' of element ''{1}'' in server capabilities. Got ''{2}''", attr, elem, val));
}
/**
* Returns the max number of objects in a changeset. -1 if either the capabilities
* don't include this parameter or if the parameter value is illegal (not a number,
* a negative number)
*
* @return the max number of objects in a changeset
*/
public int getMaxChangesetSize() {
String v = get("changesets", "maximum_elements");
if (v != null) {
try {
int n = Integer.parseInt(v);
if (n <= 0) {
warnIllegalValue("changesets", "maximum_elements", n);
} else {
return n;
}
} catch (NumberFormatException e) {
warnIllegalValue("changesets", "maximum_elements", v);
}
}
return -1;
}
/**
* Returns the max number of nodes in a way. -1 if either the capabilities
* don't include this parameter or if the parameter value is illegal (not a number,
* a negative number)
*
* @return the max number of nodes in a way
*/
public long getMaxWayNodes() {
String v = get("waynodes", "maximum");
if (v != null) {
try {
long n = Long.parseLong(v);
if (n <= 0) {
warnIllegalValue("waynodes", "maximum", n);
} else {
return n;
}
} catch (NumberFormatException e) {
warnIllegalValue("waynodes", "maximum", v);
}
}
return -1;
}
/**
* Checks if the given URL is blacklisted by one of the of the regular expressions.
* @param url Imagery URL to check
* @return {@code true} if URL is blacklisted, {@code false} otherwise
*/
public boolean isOnImageryBlacklist(String url) {
if (url != null && imageryBlacklist != null) {
for (String blacklistRegex : imageryBlacklist) {
if (url.matches(blacklistRegex))
return true;
}
}
return false;
}
/**
* Returns the full list of imagery blacklist regular expressions.
* @return full list of imagery blacklist regular expressions
*/
public List<String> getImageryBlacklist() {
return Collections.unmodifiableList(imageryBlacklist);
}
/**
* A parser for the "capabilities" response XML.
* @since 7473
*/
public static final class CapabilitiesParser extends DefaultHandler {
private Capabilities capabilities;
@Override
public void startDocument() {
capabilities = new Capabilities();
}
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
for (int i = 0; i < atts.getLength(); i++) {
capabilities.put(qName, atts.getQName(i), atts.getValue(i));
}
}
/**
* Returns the read capabilities.
* @return the read capabilities
*/
public Capabilities getCapabilities() {
return capabilities;
}
/**
* Parses and returns capabilities from the given input source.
*
* @param inputSource The input source to read capabilities from
* @return the capabilities
* @throws SAXException if any SAX errors occur during processing
* @throws IOException if any I/O errors occur
* @throws ParserConfigurationException if a parser cannot be created
*/
public static Capabilities parse(InputSource inputSource) throws SAXException, IOException, ParserConfigurationException {
CapabilitiesParser parser = new CapabilitiesParser();
Utils.parseSafeSAX(inputSource, parser);
return parser.getCapabilities();
}
}
}