/* * Copyright 2013 NGDATA nv * Copyright 2008 Outerthought bvba and Schaubroeck nv * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lilyproject.runtime.conf; import javax.xml.parsers.ParserConfigurationException; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.lilyproject.util.io.IOUtils; import org.lilyproject.util.location.Location; import org.lilyproject.util.location.LocationImpl; import org.lilyproject.util.xml.LocalSAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; public class XmlConfBuilder { private static final String CONFIG_NAMESPACE = "http://lilyproject.org/configuration"; private XmlConfBuilder() { } public static ConfImpl build(File file) throws IOException, SAXException, ParserConfigurationException { InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(file)); return build(is, file.getAbsolutePath()); } finally { IOUtils.closeQuietly(is, file.getAbsolutePath()); } } public static ConfImpl build(InputStream is, String location) throws IOException, SAXException, ParserConfigurationException { InputSource inputSource = new InputSource(is); inputSource.setSystemId(location); XMLReader xmlReader = LocalSAXParserFactory.newXmlReader(); ConfigurationHandler configHandler = new ConfigurationHandler(); xmlReader.setContentHandler(configHandler); xmlReader.parse(inputSource); return configHandler.rootConfig; } private static class ConfigurationHandler implements ContentHandler { private List<ConfImpl> configs = new ArrayList<ConfImpl>(); private ConfImpl rootConfig; private List<StringBuilder> charBuffers = new ArrayList<StringBuilder>(); private Locator locator; public void setDocumentLocator(Locator locator) { this.locator = locator; } public void startDocument() throws SAXException { } public void endDocument() throws SAXException { } public void startPrefixMapping(String prefix, String uri) throws SAXException { } public void endPrefixMapping(String prefix) throws SAXException { } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if (uri.equals("")) { Location location = locator == null ? Location.UNKNOWN : new LocationImpl(null, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber()); ConfImpl config = new ConfImpl(localName, location); configs.add(config); for (int i = 0; i < atts.getLength(); i++) { if (atts.getURI(i).equals("")) { config.addAttribute(atts.getLocalName(i), atts.getValue(i)); } else if (atts.getURI(i).equals(CONFIG_NAMESPACE)) { if (atts.getLocalName(i).equals("inherit")) { String inheritString = atts.getValue(i); ConfImpl.Inheritance inherit; if (inheritString.equals("none")) { inherit = ConfImpl.Inheritance.NONE; } else if (inheritString.equals("shallow")) { inherit = ConfImpl.Inheritance.SHALLOW; } else if (inheritString.equals("deep")) { inherit = ConfImpl.Inheritance.DEEP; } else { throw new SAXException("Invalid value in attribute " + atts.getQName(i) + ": \"" + inheritString + "\", should be one of: none, shallow, deep. At " + location); } config.setInheritance(inherit); } else if (atts.getLocalName(i).equals("inheritKey")) { config.setInheritConstraint(atts.getValue(i)); } else { throw new SAXException("Unsupported attribute: " + atts.getQName(i) + " at " + location); } } } charBuffers.add(new StringBuilder()); } else if (configs.size() == 0) { throw new SAXException("Root element of a configuration file should not be in a namespace."); } } public void endElement(String uri, String localName, String qName) throws SAXException { if (uri.equals("")) { ConfImpl config = configs.remove(configs.size() - 1); String textualContent = charBuffers.remove(charBuffers.size() - 1).toString().trim(); if (textualContent.length() > 0) { if (config.getChildren().size() > 0) { throw new SAXException("Mixed content is not allowed in configuration element " + config.getName() + " at " + config.getLocation()); } config.setValue(textualContent); } if (configs.size() > 0) { configs.get(configs.size() - 1).addChild(config); } else { rootConfig = config; } } } public void characters(char[] ch, int start, int length) throws SAXException { charBuffers.get(charBuffers.size() - 1).append(ch, start, length); } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } public void processingInstruction(String target, String data) throws SAXException { } public void skippedEntity(String name) throws SAXException { } } }