blob: 2221298c8f4bc983865e9ce2b091b9f67fafb14c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2011 Composent, Inc. and others. All rights reserved. This
* program and the accompanying materials are made available under the terms of
* the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Composent, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.internal.osgi.services.remoteserviceadmin;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class EndpointDescriptionParser {
private static List<String> multiValueTypes;
static {
multiValueTypes = Arrays.asList(new String[] { "String", "Long", //$NON-NLS-1$ //$NON-NLS-2$
"long", "Double", "double", "float", "Float", "int", "Integer", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
"byte", "Byte", "char", "Character", "boolean", "Boolean", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
"short", "Short" }); //$NON-NLS-1$ //$NON-NLS-2$
}
private static final String ENDPOINT_DESCRIPTIONS = "endpoint-descriptions"; //$NON-NLS-1$
private static final String ENDPOINT_DESCRIPTION = "endpoint-description"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY = "property"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_NAME = "name"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_VALUE = "value"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_VALUETYPE = "value-type"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_ARRAY = "array"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_LIST = "list"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_SET = "set"; //$NON-NLS-1$
private static final String ENDPOINT_PROPERTY_XML = "xml"; //$NON-NLS-1$
public static String[] noAttributes = new String[0];
private XMLReader xmlReader;
class IgnoringHandler extends AbstractHandler {
public IgnoringHandler(AbstractHandler parent) {
super(parent);
this.elementHandled = "IgnoringAll"; //$NON-NLS-1$
}
public void startElement(String name, Attributes attributes) {
noSubElements(name, attributes);
}
}
/**
* Abstract base class for content handlers
*/
abstract class AbstractHandler extends DefaultHandler {
protected ContentHandler parentHandler = null;
protected String elementHandled = null;
protected StringBuffer characters = null; // character data inside an
// element
public AbstractHandler() {
// Empty constructor for a root handler
}
public AbstractHandler(ContentHandler parentHandler) {
this.parentHandler = parentHandler;
xmlReader.setContentHandler(this);
}
public AbstractHandler(ContentHandler parentHandler,
String elementHandled) {
this.parentHandler = parentHandler;
xmlReader.setContentHandler(this);
this.elementHandled = elementHandled;
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
finishCharacters();
String name = makeSimpleName(localName, qName);
startElement(name, attributes);
}
public abstract void startElement(String name, Attributes attributes)
throws SAXException;
public void invalidElement(String name, Attributes attributes) {
unexpectedElement(this, name, attributes);
new IgnoringHandler(this);
}
public void endElement(String namespaceURI, String localName,
String qName) {
finishCharacters();
finished();
// Restore the parent content handler
xmlReader.setContentHandler(parentHandler);
}
/**
* An implementation for startElement when there are no sub-elements
*/
protected void noSubElements(String name, Attributes attributes) {
unexpectedElement(this, name, attributes);
// Create a new handler to ignore subsequent nested elements
new IgnoringHandler(this);
}
/*
* Save up character data until endElement or nested startElement
*
* @see org.xml.sax.ContentHandler#characters
*/
public void characters(char[] chars, int start, int length) {
if (this.characters == null) {
this.characters = new StringBuffer();
}
this.characters.append(chars, start, length);
}
// Consume the characters accumulated in this.characters.
// Called before startElement or endElement
private String finishCharacters() {
// common case -- no characters or only whitespace
if (this.characters == null || this.characters.length() == 0) {
return null;
}
if (allWhiteSpace(this.characters)) {
this.characters.setLength(0);
return null;
}
// process the characters
try {
String trimmedChars = this.characters.toString().trim();
if (trimmedChars.length() == 0) {
// this shouldn't happen due to the test for allWhiteSpace
// above
System.err.println("Unexpected non-whitespace characters: " //$NON-NLS-1$
+ trimmedChars);
return null;
}
processCharacters(trimmedChars);
return trimmedChars;
} finally {
this.characters.setLength(0);
}
}
// Method to override in the handler of an element with CDATA.
protected void processCharacters(String data) {
if (data.length() > 0)
unexpectedCharacterData(this, data);
}
private boolean allWhiteSpace(StringBuffer sb) {
int length = sb.length();
for (int i = 0; i < length; i += 1)
if (!Character.isWhitespace(sb.charAt(i)))
return false;
return true;
}
/**
* Called when this element and all elements nested into it have been
* handled.
*/
protected void finished() {
// Do nothing by default
}
/*
* A name used to identify the handler.
*/
public String getName() {
return (elementHandled != null ? elementHandled : "NoName"); //$NON-NLS-1$
}
/**
* Parse the attributes of an element with only required attributes.
*/
protected String[] parseRequiredAttributes(Attributes attributes,
String[] required) {
return parseAttributes(attributes, required, noAttributes);
}
/**
* Parse the attributes of an element with a single optional attribute.
*/
protected String parseOptionalAttribute(Attributes attributes,
String name) {
return parseAttributes(attributes, noAttributes,
new String[] { name })[0];
}
/**
* Parse the attributes of an element, given the list of required and
* optional ones. Return values in same order, null for those not
* present. Log warnings for extra attributes or missing required
* attributes.
*/
protected String[] parseAttributes(Attributes attributes,
String[] required, String[] optional) {
String[] result = new String[required.length + optional.length];
for (int i = 0; i < attributes.getLength(); i += 1) {
String name = attributes.getLocalName(i);
String value = attributes.getValue(i).trim();
int j;
if ((j = indexOf(required, name)) >= 0)
result[j] = value;
else if ((j = indexOf(optional, name)) >= 0)
result[required.length + j] = value;
else
unexpectedAttribute(elementHandled, name, value);
}
for (int i = 0; i < required.length; i += 1)
checkRequiredAttribute(elementHandled, required[i], result[i]);
return result;
}
}
SAXParser getParser() throws ParserConfigurationException, SAXException {
Activator a = Activator.getDefault();
if (a == null)
return null;
SAXParserFactory factory = a.getSAXParserFactory();
if (factory == null)
throw new SAXException("Unable to acquire sax parser"); //$NON-NLS-1$
factory.setNamespaceAware(true);
factory.setValidating(false);
try {
factory.setFeature(
"http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
} catch (SAXException se) {
// some parsers may not support string interning
}
SAXParser theParser = factory.newSAXParser();
if (theParser == null) {
throw new SAXException("Unable to create sax parser"); //$NON-NLS-1$
}
xmlReader = theParser.getXMLReader();
return theParser;
}
abstract class RootHandler extends AbstractHandler {
public RootHandler() {
super();
}
public void initialize(DocHandler document, String rootName,
Attributes attributes) {
this.parentHandler = document;
this.elementHandled = rootName;
handleRootAttributes(attributes);
}
protected abstract void handleRootAttributes(Attributes attributes);
}
class DocHandler extends AbstractHandler {
RootHandler rootHandler;
public DocHandler(String rootName, RootHandler rootHandler) {
super(null, rootName);
this.rootHandler = rootHandler;
}
public void startElement(String name, Attributes attributes) {
if (name.equals(elementHandled)) {
rootHandler.initialize(this, name, attributes);
xmlReader.setContentHandler(rootHandler);
} else
noSubElements(name, attributes);
}
}
class EndpointDescriptionDocHandler extends DocHandler {
public EndpointDescriptionDocHandler(String rootName,
RootHandler rootHandler) {
super(rootName, rootHandler);
}
public void processingInstruction(String target, String data)
throws SAXException {
// do nothing
}
}
class EndpointDescriptionsHandler extends RootHandler {
private List<EndpointDescription> endpointDescriptions = new ArrayList<EndpointDescription>();
protected void handleRootAttributes(Attributes attributes) {
}
public void startElement(String name, Attributes attributes)
throws SAXException {
if (ENDPOINT_DESCRIPTION.equals(name))
new EndpointDescriptionHandler(this, attributes,
endpointDescriptions);
else
invalidElement(name, attributes);
}
public void endElement(String namespaceURI, String localName,
String qName) {
if (elementHandled.equals(localName))
super.endElement(namespaceURI, localName, qName);
}
public List<EndpointDescription> getEndpointDescriptions() {
return endpointDescriptions;
}
}
class EndpointDescriptionHandler extends AbstractHandler {
private Map<String, Object> properties;
private List<EndpointDescription> descriptions;
public EndpointDescriptionHandler(ContentHandler parentHandler,
Attributes attributes, List<EndpointDescription> descriptions) {
super(parentHandler, ENDPOINT_DESCRIPTION);
this.properties = new TreeMap<String, Object>(
String.CASE_INSENSITIVE_ORDER);
this.descriptions = descriptions;
}
public void startElement(String name, Attributes attributes)
throws SAXException {
if (ENDPOINT_PROPERTY.equals(name))
new EndpointPropertyHandler(this, attributes, properties);
}
public void endElement(String namespaceURI, String localName,
String qName) {
if (elementHandled.equals(localName)) {
descriptions.add(new EndpointDescription(properties));
super.endElement(namespaceURI, localName, qName);
}
}
}
private Object createValue(String valueType, String value) {
if (value == null)
return null;
if (valueType.equals("String")) { //$NON-NLS-1$
return value;
} else if (valueType.equals("long") || valueType.equals("Long")) { //$NON-NLS-1$ //$NON-NLS-2$
return Long.valueOf(value);
} else if (valueType.equals("double") || valueType.equals("Double")) { //$NON-NLS-1$ //$NON-NLS-2$
return Double.valueOf(value);
} else if (valueType.equals("float") || valueType.equals("Float")) { //$NON-NLS-1$ //$NON-NLS-2$
return Float.valueOf(value);
} else if (valueType.equals("int") || valueType.equals("Integer")) { //$NON-NLS-1$ //$NON-NLS-2$
return Integer.valueOf(value);
} else if (valueType.equals("byte") || valueType.equals("Byte")) { //$NON-NLS-1$ //$NON-NLS-2$
return Byte.valueOf(value);
} else if (valueType.equals("char") //$NON-NLS-1$
|| valueType.equals("Character")) { //$NON-NLS-1$
char[] chars = new char[1];
value.getChars(0, 1, chars, 0);
return Character.valueOf(chars[0]);
} else if (valueType.equals("boolean") //$NON-NLS-1$
|| valueType.equals("Boolean")) { //$NON-NLS-1$
return Boolean.valueOf(value);
} else if (valueType.equals("short") || valueType.equals("Short")) { //$NON-NLS-1$ //$NON-NLS-2$
return Short.valueOf(value);
}
return null;
}
abstract class MultiValueHandler extends AbstractHandler {
protected String valueType;
public MultiValueHandler(ContentHandler parentHandler,
String elementHandled, String valueType) {
super(parentHandler, elementHandled);
this.valueType = valueType;
}
public void startElement(String name, Attributes attributes)
throws SAXException {
if (ENDPOINT_PROPERTY_VALUE.equals(name))
characters = new StringBuffer();
}
public void endElement(String namespaceURI, String localName,
String qName) {
if (ENDPOINT_PROPERTY_VALUE.equals(localName)) {
Object value = createValue(
valueType,
processValue((characters == null) ? null : characters
.toString()));
if (value != null)
addValue(value);
characters = null;
} else if (elementHandled.equals(localName))
super.endElement(namespaceURI, localName, qName);
}
private String processValue(String characters) {
if (characters == null || characters.length() == 0)
return null;
if (valueType.equals("String")) //$NON-NLS-1$
return characters;
return characters.trim();
}
protected abstract void addValue(Object value);
public abstract Object getValues();
}
class ArrayMultiValueHandler extends MultiValueHandler {
private List<Object> values = new ArrayList<Object>();
public ArrayMultiValueHandler(ContentHandler parentHandler,
String elementHandled, String valueType) {
super(parentHandler, elementHandled, valueType);
}
protected Object[] createEmptyArrayOfType() {
if (valueType.equals("String")) //$NON-NLS-1$
return new String[] {};
else if (valueType.equals("long") || valueType.equals("Long")) //$NON-NLS-1$ //$NON-NLS-2$
return new Long[] {};
else if (valueType.equals("double") || valueType.equals("Double")) //$NON-NLS-1$ //$NON-NLS-2$
return new Double[] {};
else if (valueType.equals("float") || valueType.equals("Float")) //$NON-NLS-1$ //$NON-NLS-2$
return new Double[] {};
else if (valueType.equals("int") || valueType.equals("Integer")) //$NON-NLS-1$ //$NON-NLS-2$
return new Integer[] {};
else if (valueType.equals("byte") || valueType.equals("Byte")) //$NON-NLS-1$ //$NON-NLS-2$
return new Byte[] {};
else if (valueType.equals("char") //$NON-NLS-1$
|| valueType.equals("Character")) //$NON-NLS-1$
return new Character[] {};
else if (valueType.equals("boolean") //$NON-NLS-1$
|| valueType.equals("Boolean")) //$NON-NLS-1$
return new Boolean[] {};
else if (valueType.equals("short") || valueType.equals("Short")) //$NON-NLS-1$ //$NON-NLS-2$
return new Short[] {};
else
return null;
}
public Object getValues() {
return values.toArray(createEmptyArrayOfType());
}
protected void addValue(Object value) {
values.add(value);
}
}
class ListMultiValueHandler extends MultiValueHandler {
private List<Object> values = new ArrayList<Object>();
public ListMultiValueHandler(ContentHandler parentHandler,
String elementHandled, String valueType) {
super(parentHandler, elementHandled, valueType);
}
public Object getValues() {
return values;
}
protected void addValue(Object value) {
values.add(value);
}
}
class SetMultiValueHandler extends MultiValueHandler {
private Set<Object> values = new HashSet<Object>();
public SetMultiValueHandler(ContentHandler parentHandler,
String elementHandled, String valueType) {
super(parentHandler, elementHandled, valueType);
}
public Object getValues() {
return values;
}
protected void addValue(Object value) {
values.add(value);
}
}
class XMLValueHandler extends AbstractHandler {
private Map<String, String> nsPrefixMap = new HashMap<String, String>();
private StringBuffer buf;
public XMLValueHandler(ContentHandler parentHandler) {
super(parentHandler, ENDPOINT_PROPERTY_XML);
buf = new StringBuffer();
}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
nsPrefixMap.put(uri, prefix);
}
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
buf.append("<").append(qName); //$NON-NLS-1$
for (Iterator<String> i = nsPrefixMap.keySet().iterator(); i
.hasNext();) {
String nsURI = (String) i.next();
String prefix = (String) nsPrefixMap.get(nsURI);
i.remove();
if (nsURI != null) {
buf.append(" xmlns"); //$NON-NLS-1$
if (prefix != null)
buf.append(":").append(prefix); //$NON-NLS-1$
buf.append("=\"").append(nsURI).append("\""); //$NON-NLS-1$ //$NON-NLS-2$
}
}
for (int i = 0; i < attributes.getLength(); i++) {
buf.append(" "); //$NON-NLS-1$
buf.append(attributes.getQName(i))
.append("=\"").append(attributes.getValue(i)).append("\""); //$NON-NLS-1$ //$NON-NLS-2$
}
buf.append(">"); //$NON-NLS-1$
characters = new StringBuffer();
}
public void startElement(String name, Attributes attributes)
throws SAXException {
// not used
}
public void endElement(String namespaceURI, String localName,
String qName) {
if (elementHandled.equals(localName)) {
super.endElement(namespaceURI, localName, qName);
} else {
if (characters != null)
buf.append(characters);
buf.append("</").append(qName).append(">"); //$NON-NLS-1$ //$NON-NLS-2$
characters = null;
}
}
public String getXML() {
return buf.toString();
}
}
class EndpointPropertyHandler extends AbstractHandler {
private Map<String, Object> properties;
private String name;
private String valueType = "String"; //$NON-NLS-1$
private Object value;
private MultiValueHandler multiValueHandler;
private XMLValueHandler xmlValueHandler;
public EndpointPropertyHandler(ContentHandler parentHandler,
Attributes attributes, Map<String, Object> properties)
throws SAXException {
super(parentHandler, ENDPOINT_PROPERTY);
name = parseRequiredAttributes(attributes,
new String[] { ENDPOINT_PROPERTY_NAME })[0];
String strValue = parseOptionalAttribute(attributes,
ENDPOINT_PROPERTY_VALUE);
String vt = parseOptionalAttribute(attributes,
ENDPOINT_PROPERTY_VALUETYPE);
if (vt != null) {
if (!multiValueTypes.contains(vt))
throw new SAXException("property element valueType=" + vt //$NON-NLS-1$
+ " not allowed"); //$NON-NLS-1$
this.valueType = vt;
}
this.properties = properties;
if (strValue != null) {
value = createValue(this.valueType, strValue);
if (isValidProperty(name, value))
this.properties.put(name, value);
}
}
public void startElement(String name, Attributes attributes)
throws SAXException {
// Should not happen if value is non-null
if (value != null)
throw new SAXException(
"property element has both value attribute and sub-element"); //$NON-NLS-1$
if (ENDPOINT_PROPERTY_ARRAY.equals(name)) {
if (multiValueHandler == null)
multiValueHandler = new ArrayMultiValueHandler(this,
ENDPOINT_PROPERTY_ARRAY, valueType);
else
duplicateElement(this, name, attributes);
} else if (ENDPOINT_PROPERTY_LIST.equals(name)) {
if (multiValueHandler == null)
multiValueHandler = new ListMultiValueHandler(this,
ENDPOINT_PROPERTY_LIST, valueType);
else
duplicateElement(this, name, attributes);
} else if (ENDPOINT_PROPERTY_SET.equals(name)) {
if (multiValueHandler == null)
multiValueHandler = new SetMultiValueHandler(this,
ENDPOINT_PROPERTY_SET, valueType);
else
duplicateElement(this, name, attributes);
} else if (ENDPOINT_PROPERTY_XML.equals(name)) {
// xml
if (xmlValueHandler == null)
xmlValueHandler = new XMLValueHandler(this);
else
duplicateElement(this, name, attributes);
} else
invalidElement(name, attributes);
}
public void endElement(String namespaceURI, String localName,
String qName) {
if (elementHandled.equals(localName)) {
if (multiValueHandler != null) {
properties.put(name, multiValueHandler.getValues());
multiValueHandler = null;
} else if (xmlValueHandler != null) {
properties.put(name, xmlValueHandler.getXML());
xmlValueHandler = null;
}
super.endElement(namespaceURI, localName, qName);
}
}
private boolean isValidProperty(String name, Object value) {
return (name != null && value != null);
}
}
public class EndpointDescription {
private Map<String, Object> properties;
public EndpointDescription(Map<String, Object> properties) {
this.properties = properties;
}
public Map<String, Object> getProperties() {
return properties;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("EndpointDescription [properties="); //$NON-NLS-1$
builder.append(properties);
builder.append("]"); //$NON-NLS-1$
return builder.toString();
}
}
public synchronized void parse(InputStream input) throws IOException {
try {
getParser();
EndpointDescriptionsHandler endpointDescriptionsHandler = new EndpointDescriptionsHandler();
xmlReader.setContentHandler(new EndpointDescriptionDocHandler(
ENDPOINT_DESCRIPTIONS, endpointDescriptionsHandler));
xmlReader.parse(new InputSource(input));
endpointDescriptions = endpointDescriptionsHandler
.getEndpointDescriptions();
} catch (SAXException e) {
throw new IOException(e.getMessage());
} catch (ParserConfigurationException e) {
throw new IOException(e.getMessage());
} finally {
input.close();
}
}
public static String makeSimpleName(String localName, String qualifiedName) {
if (localName != null && localName.length() > 0)
return localName;
int nameSpaceIndex = qualifiedName.indexOf(":"); //$NON-NLS-1$
return (nameSpaceIndex == -1 ? qualifiedName : qualifiedName
.substring(nameSpaceIndex + 1));
}
public void unexpectedElement(AbstractHandler handler, String element,
Attributes attributes) {
}
public void unexpectedCharacterData(AbstractHandler handler, String cdata) {
}
public void unexpectedAttribute(String element, String attribute,
String value) {
}
static int indexOf(String[] array, String value) {
for (int i = 0; i < array.length; i += 1) {
if (value == null ? array[i] == null : value.equals(array[i])) {
return i;
}
}
return -1;
}
public void checkRequiredAttribute(String element, String name, Object value) {
}
public void duplicateElement(AbstractHandler handler, String element,
Attributes attributes) {
// ignore the duplicate element entirely because we have already logged
// it
new IgnoringHandler(handler);
}
private List<EndpointDescription> endpointDescriptions;
public List<EndpointDescription> getEndpointDescriptions() {
return endpointDescriptions;
}
}