package the8472.utils; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.IntStream; import java.util.stream.Stream; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class ConfigReader { public static class ParseException extends RuntimeException { public ParseException(Exception cause) { super(cause); } }; FilesystemNotifications notifications; Path configFile; Schema schema; Supplier<InputStream> defaults; Document current; public ConfigReader(Path toRead, Supplier<InputStream> defaults, Supplier<InputStream> schemaSource) { configFile = toRead; this.defaults = defaults; try { buildSchema(schemaSource.get()); } catch (SAXException e1) { throw new RuntimeException(e1); } if(!Files.exists(configFile)) { try { Files.copy(defaults.get(), configFile); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void buildSchema(InputStream source) throws SAXException { // create a SchemaFactory capable of understanding WXS schemas SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // load a WXS schema, represented by a Schema instance Source schemaFile = new StreamSource(source); schema = schemaFactory.newSchema(schemaFile); } List<Runnable> callbacks = new ArrayList<>(); public void addChangeCallback(Runnable callback) { callbacks.add(callback); } public void registerFsNotifications(FilesystemNotifications notifier) { notifications = notifier; notifier.addRegistration(configFile, (path, kind) -> { if(path.equals(configFile)) { current = readFile(configFile); callbacks.forEach(Runnable::run); } }); } private void write(Path source) { /* JSONObject obj = new JSONObject(toWrite); try { Files.write(configFile, obj.toJSONString().getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } */ } public Document read() { if(current == null) readConfig(); return current; } void readConfig() { current = readFile(configFile); } public Optional<String> get(XPathExpression expr) { Node result; try { result = (Node) expr.evaluate(current, XPathConstants.NODE); } catch (XPathExpressionException e) { throw new RuntimeException(e); } if(result == null) return Optional.empty(); return Optional.of(result.getTextContent()); } public Optional<Boolean> getBoolean(String path) { return get(XMLUtils.buildXPath(path)).map(str -> str.equals("true") || str.equals("1")); } public Optional<Long> getLong(String path) { return get(XMLUtils.buildXPath(path)).map(Long::valueOf); } public Stream<String> getAll(XPathExpression path) { NodeList result; try { result = (NodeList) path.evaluate(current, XPathConstants.NODESET); } catch (XPathExpressionException e) { throw new RuntimeException(e); } if(result == null) return Stream.empty(); return IntStream.range(0, result.getLength()).mapToObj(result::item).map(Node::getTextContent); } Document readFile(Path p) { Document document = null; try { // parse an XML document into a DOM tree DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder parser = factory.newDocumentBuilder(); document = parser.parse(p.toFile()); // create a Validator instance, which can be used to validate an instance document Validator validator = schema.newValidator(); // validate the DOM tree validator.validate(new DOMSource(document)); } catch (SAXException | IOException | ParserConfigurationException e) { throw new ParseException(e); } return document; } }