/*
* 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 {
}
}
}