diff options
Diffstat (limited to '')
-rw-r--r-- | sca-java-2.x/tags/2.0.1-RC1/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java b/sca-java-2.x/tags/2.0.1-RC1/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java new file mode 100644 index 0000000000..f546e44dfb --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/extensibility/src/main/java/org/apache/tuscany/sca/extensibility/ServiceDeclarationParser.java @@ -0,0 +1,375 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tuscany.sca.extensibility; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.xml.namespace.QName; + +/** + * Parser for the service descriptors. The syntax of the service declaration is similar with the OSGi + * headers with the following exceptions: + * <ul> + * <li>Tuscany uses , and ; as the separator for attibutes + * <li>Tuscany + */ +public class ServiceDeclarationParser { + + // private static final String PATH_SEPARATOR = ","; // OSGi style + private static final String PATH_SEPARATOR = "|"; + + // private static final String SEGMENT_SEPARATOR = ";"; // OSGi style + private static final String SEGMENT_SEPARATOR = ";,"; + + private static final String ATTRIBUTE_SEPARATOR = "="; + private static final String DIRECTIVE_SEPARATOR = ":="; + + private static final char QUOTE_CHAR = '"'; + private static final String QUOTE = "\""; + + // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2, + // path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + public static List<Descriptor> parse(String header) { + + if (header != null) { + if (header.length() == 0) { + throw new IllegalArgumentException("A header cannot be an empty string."); + } + + String[] clauseStrings = parseDelimitedString(header, PATH_SEPARATOR); + + List<Descriptor> completeList = new ArrayList<Descriptor>(); + for (int i = 0; (clauseStrings != null) && (i < clauseStrings.length); i++) { + completeList.add(parseClause(clauseStrings[i])); + } + + return completeList; + } + + return null; + + } + + /** + * Parse the declaration into a map of name/value pairs. The class name is added under "class" + * and directives are added using @<directiveName> as the key. + * @param declaration + * @return A map of attributes + */ + public static Map<String, String> parseDeclaration(String declaration) { + List<Descriptor> descriptors = parse(declaration); + Descriptor descriptor = descriptors.get(0); + Map<String, String> map = new HashMap<String, String>(); + map.putAll(descriptor.getAttributes()); + map.put("class", descriptor.getValue()); + for (Map.Entry<String, String> e : descriptor.getDirectives().entrySet()) { + // For directives, add @ as the prefix for the key + map.put("@" + e.getKey(), e.getValue()); + } + return map; + } + + // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + private static Descriptor parseClause(String clauseString) throws IllegalArgumentException { + // Break string into semi-colon delimited pieces. + String[] pieces = parseDelimitedString(clauseString, SEGMENT_SEPARATOR); + + // Count the number of different paths; paths + // will not have an '=' in their string. This assumes + // that paths come first, before directives and + // attributes. + int pathCount = 0; + for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) { + if (pieces[pieceIdx].indexOf('=') >= 0) { + break; + } + pathCount++; + } + + // Create an array of paths. + String[] paths = new String[pathCount]; + System.arraycopy(pieces, 0, paths, 0, pathCount); + + // Parse the directives/attributes. + Map<String, String> dirsMap = new HashMap<String, String>(); + Map<String, String> attrsMap = new HashMap<String, String>(); + int idx = -1; + String sep = null; + for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) { + // Check if it is a directive. + if ((idx = pieces[pieceIdx].indexOf(DIRECTIVE_SEPARATOR)) >= 0) { + sep = DIRECTIVE_SEPARATOR; + } + // Check if it is an attribute. + else if ((idx = pieces[pieceIdx].indexOf(ATTRIBUTE_SEPARATOR)) >= 0) { + sep = ATTRIBUTE_SEPARATOR; + } + // It is an error. + else { + throw new IllegalArgumentException("Not a directive/attribute: " + clauseString); + } + + String key = pieces[pieceIdx].substring(0, idx).trim(); + String value = pieces[pieceIdx].substring(idx + sep.length()).trim(); + + // Remove quotes, if value is quoted. + if (value.startsWith(QUOTE) && value.endsWith(QUOTE)) { + value = value.substring(1, value.length() - 1); + } + + // Save the directive/attribute in the appropriate array. + if (sep.equals(DIRECTIVE_SEPARATOR)) { + // Check for duplicates. + if (dirsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate directive: " + key); + } + dirsMap.put(key, value); + } else { + // Check for duplicates. + if (attrsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate attribute: " + key); + } + attrsMap.put(key, value); + } + } + + StringBuffer path = new StringBuffer(); + for (int i = 0; i < paths.length; i++) { + path.append(paths[i]); + if (i != paths.length - 1) { + path.append(';'); + } + } + + Descriptor descriptor = new Descriptor(); + descriptor.text = clauseString; + descriptor.value = path.toString(); + descriptor.valueComponents = paths; + descriptor.attributes = attrsMap; + descriptor.directives = dirsMap; + + return descriptor; + } + + /** + * Parses delimited string and returns an array containing the tokens. This + * parser obeys quotes, so the delimiter character will be ignored if it is + * inside of a quote. This method assumes that the quote character is not + * included in the set of delimiter characters. + * @param value the delimited string to parse. + * @param delim the characters delimiting the tokens. + * @return an array of string tokens or null if there were no tokens. + **/ + private static String[] parseDelimitedString(String value, String delim) { + if (value == null) { + value = ""; + } + + List<String> list = new ArrayList<String>(); + + int CHAR = 1; + int DELIMITER = 2; + int STARTQUOTE = 4; + int ENDQUOTE = 8; + + StringBuffer sb = new StringBuffer(); + + int expecting = (CHAR | DELIMITER | STARTQUOTE); + + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + + boolean isDelimiter = (delim.indexOf(c) >= 0); + boolean isQuote = (c == QUOTE_CHAR); + + if (isDelimiter && ((expecting & DELIMITER) > 0)) { + list.add(sb.toString().trim()); + sb.delete(0, sb.length()); + expecting = (CHAR | DELIMITER | STARTQUOTE); + } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { + sb.append(c); + expecting = CHAR | ENDQUOTE; + } else if (isQuote && ((expecting & ENDQUOTE) > 0)) { + sb.append(c); + expecting = (CHAR | STARTQUOTE | DELIMITER); + } else if ((expecting & CHAR) > 0) { + sb.append(c); + } else { + throw new IllegalArgumentException("Invalid delimited string: " + value); + } + } + + if (sb.length() > 0) { + list.add(sb.toString().trim()); + } + + return (String[])list.toArray(new String[list.size()]); + } + + public static class Descriptor { + private String text; + private String value; + private String[] valueComponents; + private Map<String, String> attributes; + private Map<String, String> directives; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String[] getValueComponents() { + return valueComponents; + } + + public void setValueComponents(String[] valueComponents) { + this.valueComponents = valueComponents; + } + + public Map<String, String> getAttributes() { + return attributes; + } + + public Map<String, String> getDirectives() { + return directives; + } + + public String toString() { + return text; + } + + } + + /** + * Returns a QName object from a QName expressed as {ns}name + * or ns#name. + * + * @param qname + * @return + */ + public static QName getQName(String qname) { + if (qname == null) { + return null; + } + qname = qname.trim(); + if (qname.startsWith("{")) { + int h = qname.indexOf('}'); + if (h != -1) { + return new QName(qname.substring(1, h), qname.substring(h + 1)); + } + } else { + int h = qname.indexOf('#'); + if (h != -1) { + return new QName(qname.substring(0, h), qname.substring(h + 1)); + } + } + return new QName(qname); + } + + public static Collection<Map<String, String>> load(final URL url, boolean isPropertyFile) throws IOException { + Collection<Map<String, String>> descriptors = new ArrayList<Map<String, String>>(); + + // Allow privileged access to open URL stream. Add FilePermission to added to security + // policy file. + InputStream is; + try { + is = AccessController.doPrivileged(new PrivilegedExceptionAction<InputStream>() { + public InputStream run() throws IOException { + URLConnection connection = url.openConnection(); + // TUSCANY-2539 + // Don't cache connections by default to stop Tuscany locking contribution jar files + // done here as this is one of the first places we open a stream and the only way to + // set the default is to set it on an instance of URLConnection + connection.setDefaultUseCaches(false); + connection.setUseCaches(false); + return url.openStream(); + } + }); + } catch (PrivilegedActionException e) { + throw (IOException)e.getException(); + } + if (isPropertyFile) { + // Load as a property file + Properties props = new Properties(); + props.load(is); + is.close(); + for (Map.Entry<Object, Object> e : props.entrySet()) { + Map<String, String> attributes = new HashMap<String, String>(); + String key = (String)e.getKey(); + String value = (String)e.getValue(); + // Unfortunately, the xalan file only has the classname + if (value == null || "".equals(value)) { + value = key; + key = ""; + } + if (!"".equals(key)) { + attributes.put(key, value); + attributes.put("uri", key); + } + attributes.putAll(parseDeclaration(value)); + descriptors.add(attributes); + } + return descriptors; + } + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(is)); + while (true) { + String line = reader.readLine(); + if (line == null) + break; + line = line.trim(); + if (!line.startsWith("#") && !"".equals(line)) { + String reg = line.trim(); + + Map<String, String> attributes = parseDeclaration(reg); + descriptors.add(attributes); + } + } + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // Ignore + } + } + } + return descriptors; + } + +} |