// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.graphview.core.access;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.openstreetmap.josm.plugins.graphview.core.data.Tag;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* utility class that can read access rulesets from xml files
*/
public final class AccessRulesetReader {
public static class AccessRulesetSyntaxException extends IOException {
private static final long serialVersionUID = 1L;
public AccessRulesetSyntaxException(String message) {
super(message);
}
public AccessRulesetSyntaxException(Throwable t) {
super(t.toString());
}
}
/** private constructor to prevent instantiation */
private AccessRulesetReader() { }
public static AccessRuleset readAccessRuleset(InputStream inputStream)
throws AccessRulesetSyntaxException, IOException {
RulesetHandler rulesetHandler = new RulesetHandler();
try {
XMLReader reader = XMLReaderFactory.createXMLReader();
InputSource input = new InputSource(inputStream);
reader.setContentHandler(rulesetHandler);
reader.setErrorHandler(null);
reader.parse(input);
} catch (SAXException e) {
throw new AccessRulesetSyntaxException(e);
}
return rulesetHandler.getAccessRuleset();
}
private static class RulesetHandler extends DefaultHandler {
private static class AccessClass {
final String name;
final AccessClass parent;
AccessClass(String name, AccessClass parent) {
this.name = name;
this.parent = parent;
}
List<String> getAncestorHierarchy() {
List<String> names;
if (parent == null) {
names = new LinkedList<>();
} else {
names = parent.getAncestorHierarchy();
}
names.add(0, name);
return names;
}
}
private final Collection<AccessClass> accessClasses = new LinkedList<>();
private final Collection<Tag> baseTags = new LinkedList<>();
private enum Section { NONE, CLASSES, BASETAGS, IMPLICATIONS }
private Section currentSection = Section.NONE;
private AccessClass currentAccessClass = null;
private ImplicationXMLReader implicationReader = null;
private final List<Implication> implications = new LinkedList<>();
/** returns the AccessRuleset that was read */
AccessRuleset getAccessRuleset() {
return new AccessRuleset() {
@Override
public List<String> getAccessHierarchyAncestors(String transportMode) {
for (AccessClass accessClass : accessClasses) {
if (accessClass.name.equals(transportMode)) {
return accessClass.getAncestorHierarchy();
}
}
return new LinkedList<>();
}
@Override
public Collection<Tag> getBaseTags() {
return baseTags;
}
@Override
public List<Implication> getImplications() {
return implications;
}
};
}
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
if (implicationReader != null) {
implicationReader.startElement(uri, localName, name, attributes);
return;
}
if ("classes".equals(name)) {
if (currentSection != Section.NONE) {
throw new SAXException(tr("Classes element below root child level"));
}
currentSection = Section.CLASSES;
} else if ("class".equals(name)) {
String className = attributes.getValue("name");
if (currentSection != Section.CLASSES) {
throw new SAXException(tr("Class element ({0}) outside classes element", className));
} else if (className == null) {
throw new SAXException(tr("Class element without name"));
}
AccessClass newAccessClass = new AccessClass(className, currentAccessClass);
accessClasses.add(newAccessClass);
currentAccessClass = newAccessClass;
} else if ("basetags".equals(name)) {
if (currentSection != Section.NONE) {
throw new SAXException(tr("Classes element below root child level"));
}
currentSection = Section.BASETAGS;
} else if ("tag".equals(name)) {
if (currentSection == Section.BASETAGS) {
baseTags.add(readTag(attributes));
} else {
throw new SAXException(tr("Tag element outside basetag and implication elements"));
}
} else if ("implications".equals(name)) {
if (currentSection != Section.NONE) {
throw new SAXException(tr("Implications element below root child level"));
}
implicationReader = new ImplicationXMLReader();
currentSection = Section.IMPLICATIONS;
}
}
private static Tag readTag(Attributes attributes) throws SAXException {
String key = attributes.getValue("k");
String value = attributes.getValue("v");
if (key == null) {
throw new SAXException(tr("Tag without key"));
} else if (value == null) {
throw new SAXException(tr("Tag without value (key is {0})", key));
}
return new Tag(key, value);
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
if (implicationReader != null && !"implications".equals(name)) {
implicationReader.endElement(uri, localName, name);
}
if ("classes".equals(name)) {
if (currentSection != Section.CLASSES) {
throw new SAXException(tr("Closed classes while it was not open"));
} else if (currentAccessClass != null) {
throw new SAXException(tr("Closed classes element before all class elements were closed"));
}
currentSection = Section.NONE;
} else if ("class".equals(name)) {
if (currentAccessClass == null) {
throw new SAXException(tr("Closed class element while none was open"));
}
currentAccessClass = currentAccessClass.parent;
} else if ("basetags".equals(name)) {
if (currentSection != Section.BASETAGS) {
throw new SAXException(tr("Closed basetags while it was not open"));
}
currentSection = Section.NONE;
} else if ("implications".equals(name)) {
if (currentSection != Section.IMPLICATIONS) {
throw new SAXException(tr("Closed implications while it was not open"));
}
implications.addAll(implicationReader.getImplications());
implicationReader = null;
currentSection = Section.NONE;
}
}
}
}