You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/04/10 16:16:15 UTC

[53/59] [abbrv] [KARAF-2852] Merge region/core and region/command

http://git-wip-us.apache.org/repos/asf/karaf/blob/1bcdb173/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderProcessor.java
----------------------------------------------------------------------
diff --git a/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderProcessor.java b/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderProcessor.java
new file mode 100644
index 0000000..410121c
--- /dev/null
+++ b/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderProcessor.java
@@ -0,0 +1,661 @@
+/*
+ * 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.karaf.region.persist.internal.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Constants;
+
+public class ManifestHeaderProcessor
+{
+  public static final String NESTED_FILTER_ATTRIBUTE = "org.apache.aries.application.filter.attribute";
+  private static final Pattern FILTER_ATTR = Pattern.compile("(\\(!)?\\((.*?)([<>]?=)(.*?)\\)\\)?");
+  private static final String LESS_EQ_OP = "<=";
+  private static final String GREATER_EQ_OP = ">=";
+
+  /**
+   * A simple class to associate two types.
+   *
+   */
+  public static class NameValuePair {
+    private String name;
+    private Map<String,String> attributes;
+
+    public NameValuePair(String name, Map<String,String> value)
+    {
+      this.name = name;
+      this.attributes = value;
+    }
+    public String getName()
+    {
+      return name;
+    }
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public Map<String,String> getAttributes()
+    {
+      return attributes;
+    }
+    public void setAttributes(Map<String,String> value)
+    {
+      this.attributes = value;
+    }
+
+    @Override
+    public String toString(){
+      return "{"+name.toString()+"::"+attributes.toString()+"}";
+    }
+    @Override
+    public int hashCode()
+    {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+      return result;
+    }
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (this == obj) return true;
+      if (obj == null) return false;
+      if (getClass() != obj.getClass()) return false;
+      final NameValuePair other = (NameValuePair) obj;
+      if (name == null) {
+        if (other.name != null) return false;
+      } else if (!name.equals(other.name)) return false;
+      if (attributes == null) {
+    	  if (other.attributes != null) return false;
+      } else if (!attributes.equals(other.attributes)) return false;
+      return true;
+    }
+  }
+
+  /**
+   * Intended to provide a standard way to add Name/Value's to
+   * aggregations of Name/Value's.
+   *
+   */
+  public static interface NameValueCollection {
+    /**
+     * Add this Name & Value to the collection.
+     * @param n
+     * @param v
+     */
+    public void addToCollection(String n, Map<String,String> v);
+  }
+
+  /**
+   * Map of Name -> Value.
+   *
+   */
+  public static class NameValueMap extends HashMap<String, Map<String,String>> implements NameValueCollection, Map<String, Map<String,String>>{
+	private static final long serialVersionUID = -6446338858542599141L;
+
+	public void addToCollection(String n, Map<String,String> v){
+      this.put(n,v);
+    }
+
+	@Override
+	public String toString(){
+      StringBuilder sb = new StringBuilder();
+      sb.append("{");
+      boolean first=true;
+      for(Map.Entry<String, Map<String,String>> entry : this.entrySet()){
+        if(!first)sb.append(",");
+        first=false;
+        sb.append(entry.getKey()+"->"+entry.getValue());
+      }
+      sb.append("}");
+      return sb.toString();
+    }
+  }
+
+  /**
+   * List of Name/Value
+   *
+   */
+  public static class NameValueList extends ArrayList<NameValuePair> implements NameValueCollection, List<NameValuePair> {
+	private static final long serialVersionUID = 1808636823825029983L;
+
+	public void addToCollection(String n, Map<String,String> v){
+      this.add(new NameValuePair(n,v));
+    }
+	@Override
+    public String toString(){
+      StringBuffer sb = new StringBuffer();
+      sb.append("{");
+      boolean first = true;
+      for(NameValuePair nvp : this){
+        if(!first)sb.append(",");
+        first=false;
+        sb.append(nvp.toString());
+      }
+      sb.append("}");
+      return sb.toString();
+    }
+  }
+
+  /**
+   *
+   * Splits a delimiter separated string, tolerating presence of non separator commas
+   * within double quoted segments.
+   *
+   * Eg.
+   * com.ibm.ws.eba.helloWorldService;version="[1.0.0, 1.0.0]" &
+   * com.ibm.ws.eba.helloWorldService;version="1.0.0"
+   * com.ibm.ws.eba.helloWorld;version="2";bundle-version="[2,30)"
+   * com.acme.foo;weirdAttr="one;two;three";weirdDir:="1;2;3"
+   *  @param value          the value to be split
+   *  @param delimiter      the delimiter string such as ',' etc.
+   *  @return List<String>  the components of the split String in a list
+   */
+  public static List<String> split(String value, String delimiter)
+  {
+    return ManifestHeaderUtils.split(value, delimiter);
+  }
+
+
+  /**
+   * Internal method to parse headers with the format<p>
+   *   [Name](;[Name])*(;[attribute-name]=[attribute-value])*<br>
+   * Eg.<br>
+   *   rumplestiltskin;thing=value;other=something<br>
+   *   littleredridinghood
+   *   bundle1;bundle2;other=things
+   *   bundle1;bundle2
+   *
+   * @param s data to parse
+   * @return a list of NameValuePair, with the Name being the name component,
+   *         and the Value being a NameValueMap of key->value mappings.
+   */
+  private static List<NameValuePair> genericNameWithNameValuePairProcess(String s){
+    String name;
+    Map<String,String> params = null;
+    List<NameValuePair> nameValues = new ArrayList<NameValuePair>();
+    List<String> pkgs = new ArrayList<String>();
+    int index = s.indexOf(";");
+    if(index==-1){
+      name = s;
+      params = new HashMap<String, String>();
+      pkgs.add(name);
+    }else{
+      name = s.substring(0,index).trim();
+      String tail = s.substring(index+1).trim();
+
+      pkgs.add(name); // add the first package
+      StringBuilder parameters = new StringBuilder();
+
+
+      // take into consideration of multiple packages separated by ';'
+      // while they share the same attributes or directives
+      List<String> tailParts = split(tail, ";");
+      boolean firstParameter =false;
+
+      for (String part : tailParts) {
+        // if it is not a parameter and no parameter appears in front of it, it must a package
+        if (!!!(part.contains("=")))  {
+          // Need to make sure no parameter appears before the package, otherwise ignore this string
+          // as this syntax is invalid
+          if (!!!(firstParameter))
+            pkgs.add(part);
+        } else {
+          if (!!!(firstParameter))
+            firstParameter = true;
+
+          parameters.append(part + ";");
+        }
+      }
+
+      if (parameters.length() != 0) {
+        //remove the final ';' if there is one
+        if (parameters.toString().endsWith(";")) {
+
+          parameters = parameters.deleteCharAt(parameters.length() -1);
+        }
+
+        params = genericNameValueProcess(parameters.toString());
+      }
+
+    }
+    for (String pkg : pkgs) {
+      nameValues.add(new NameValuePair(pkg,params));
+    }
+
+    return nameValues;
+
+  }
+
+  /**
+   * Internal method to parse headers with the format<p>
+   *   [attribute-name]=[attribute-value](;[attribute-name]=[attribute-value])*<br>
+   * Eg.<br>
+   *   thing=value;other=something<br>
+   * <p>
+   * Note. Directives (name:=value) are represented in the map with name suffixed by ':'
+   *
+   * @param s data to parse
+   * @return a NameValueMap, with attribute-name -> attribute-value.
+   */
+  private static Map<String,String> genericNameValueProcess(String s){
+    Map<String,String> params = new HashMap<String,String>();
+    List<String> parameters = split(s, ";");
+    for(String parameter : parameters) {
+      List<String> parts = split(parameter,"=");
+      // do a check, otherwise we might get NPE
+      if (parts.size() ==2) {
+        String second = parts.get(1).trim();
+        if (second.startsWith("\"") && second.endsWith("\""))
+          second = second.substring(1,second.length()-1);
+
+        String first = parts.get(0).trim();
+
+        // make sure for directives we clear out any space as in "directive  :=value"
+        if (first.endsWith(":")) {
+            first = first.substring(0, first.length()-1).trim()+":";
+        }
+
+        params.put(first, second);
+      }
+    }
+
+    return params;
+  }
+
+  /**
+   * Processes an import/export style header.. <p>
+   *  pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value
+   *
+   * @param out The collection to add each package name + attrib map to.
+   * @param s The data to parse
+   */
+  private static void genericImportExportProcess(NameValueCollection out, String s){
+    List<String> packages = split(s, ",");
+    for(String pkg : packages){
+      List<NameValuePair> ps = genericNameWithNameValuePairProcess(pkg);
+      for (NameValuePair p : ps) {
+        out.addToCollection(p.getName(), p.getAttributes());
+      }
+    }
+  }
+
+  /**
+   * Parse an export style header.<p>
+   *   pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value2
+   * <p>
+   * Result is returned as a list, as export does allow duplicate package exports.
+   *
+   * @param s The data to parse.
+   * @return List of NameValuePairs, where each Name in the list is an exported package,
+   *         with its associated Value being a NameValueMap of any attributes declared.
+   */
+  public static List<NameValuePair> parseExportString(String s){
+    NameValueList retval = new NameValueList();
+    genericImportExportProcess(retval, s);
+    return retval;
+  }
+
+  /**
+   * Parse an export style header in a list.<p>
+   *   pkg1;attrib=value;attrib=value
+   *   pkg2;attrib=value
+   *   pkg3;attrib=value2
+   * <p>
+   * Result is returned as a list, as export does allow duplicate package exports.
+   *
+   * @param list The data to parse.
+   * @return List of NameValuePairs, where each Name in the list is an exported package,
+   *         with its associated Value being a NameValueMap of any attributes declared.
+   */
+  public static List<NameValuePair> parseExportList(List<String> list){
+    NameValueList retval = new NameValueList();
+    for(String pkg : list){
+      List<NameValuePair> ps = genericNameWithNameValuePairProcess(pkg);
+      for (NameValuePair p : ps) {
+        retval.addToCollection(p.getName(), p.getAttributes());
+      }
+    }
+    return retval;
+  }
+
+  /**
+   * Parse an import style header.<p>
+   *   pkg1;attrib=value;attrib=value,pkg2;attrib=value,pkg3;attrib=value
+   * <p>
+   * Result is returned as a set, as import does not allow duplicate package imports.
+   *
+   * @param s The data to parse.
+   * @return Map of NameValuePairs, where each Key in the Map is an imported package,
+   *         with its associated Value being a NameValueMap of any attributes declared.
+   */
+  public static Map<String, Map<String, String>> parseImportString(String s){
+    NameValueMap retval = new NameValueMap();
+    genericImportExportProcess(retval, s);
+    return retval;
+  }
+
+  /**
+   * Parse a bundle symbolic name.<p>
+   *   bundlesymbolicname;attrib=value;attrib=value
+   * <p>
+   *
+   * @param s The data to parse.
+   * @return NameValuePair with Name being the BundleSymbolicName,
+   *         and Value being any attribs declared for the name.
+   */
+  public static NameValuePair parseBundleSymbolicName(String s){
+    return genericNameWithNameValuePairProcess(s).get(0); // should just return the first one
+  }
+
+  /**
+   * Parse a version range..
+   *
+   * @param s
+   * @return VersionRange object.
+   * @throws IllegalArgumentException if the String could not be parsed as a VersionRange
+   */
+  public static VersionRange parseVersionRange(String s) throws IllegalArgumentException{
+    return new VersionRange(s);
+  }
+
+  /**
+   * Parse a version range and indicate if the version is an exact version
+   *
+   * @param s
+   * @param exactVersion
+   * @return VersionRange object.
+   * @throws IllegalArgumentException if the String could not be parsed as a VersionRange
+   */
+  public static VersionRange parseVersionRange(String s, boolean exactVersion) throws IllegalArgumentException{
+    return new VersionRange(s, exactVersion);
+  }
+
+  /**
+	 * Generate a filter from a set of attributes. This filter will be suitable
+	 * for presentation to OBR This means that, due to the way OBR works, it
+	 * will include a stanza of the form, (mandatory:<*mandatoryAttribute)
+	 * Filter strings generated by this method will therefore tend to break the
+	 * standard OSGi Filter class. The OBR stanza can be stripped out later if
+	 * required.
+	 *
+	 * @param attribs
+	 * @return filter string
+	 */
+	public static String generateFilter(Map<String, Object> attribs) {
+		StringBuilder filter = new StringBuilder("(&");
+		boolean realAttrib = false;
+		StringBuffer realAttribs = new StringBuffer();
+
+		if (attribs == null) {
+			attribs = new HashMap<String, Object>();
+		}
+
+		for (Map.Entry<String, Object> attrib : attribs.entrySet()) {
+			String attribName = attrib.getKey();
+
+			if (attribName.endsWith(":")) {
+				// skip all directives. It is used to affect the attribs on the
+				// filter xml.
+			} else if ((Constants.VERSION_ATTRIBUTE.equals(attribName))
+					|| (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(attribName))) {
+				// version and bundle-version attrib requires special
+				// conversion.
+				realAttrib = true;
+
+				VersionRange vr = ManifestHeaderProcessor
+						.parseVersionRange(attrib.getValue().toString());
+
+				filter.append("(" + attribName + ">=" + vr.getMinimumVersion());
+
+				if (vr.getMaximumVersion() != null) {
+					filter.append(")(" + attribName + "<=");
+					filter.append(vr.getMaximumVersion());
+				}
+
+				if (vr.getMaximumVersion() != null && vr.isMinimumExclusive()) {
+					filter.append(")(!(" + attribName + "=");
+					filter.append(vr.getMinimumVersion());
+					filter.append(")");
+				}
+
+				if (vr.getMaximumVersion() != null && vr.isMaximumExclusive()) {
+					filter.append(")(!(" + attribName + "=");
+					filter.append(vr.getMaximumVersion());
+					filter.append(")");
+				}
+				filter.append(")");
+
+			} else if (NESTED_FILTER_ATTRIBUTE.equals(attribName)) {
+				// Filters go in whole, no formatting needed
+				realAttrib = true;
+				filter.append(attrib.getValue());
+
+			} else if (Constants.OBJECTCLASS.equals(attribName)) {
+				realAttrib = true;
+				// objectClass has a "," separated list of interfaces
+				String[] values = attrib.getValue().toString().split(",");
+				for (String s : values)
+					filter.append("(" + Constants.OBJECTCLASS + "=" + s + ")");
+
+			} else {
+				// attribName was not version..
+				realAttrib = true;
+
+				filter.append("(" + attribName + "=" + attrib.getValue() + ")");
+				// store all attributes in order to build up the mandatory
+				// filter and separate them with ", "
+				// skip bundle-symbolic-name in the mandatory directive query
+				if (!!!Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE
+						.equals(attribName)) {
+					realAttribs.append(attribName);
+					realAttribs.append(", ");
+				}
+			}
+		}
+//		/*
+//		 * The following is how OBR makes mandatory attributes work, we require
+//		 * that the set of mandatory attributes on the export is a subset of (or
+//		 * equal to) the set of the attributes we supply.
+//		 */
+//
+//		if (realAttribs.length() > 0) {
+//			String attribStr = (realAttribs.toString()).trim();
+//			// remove the final ,
+//			if ((attribStr.length() > 0) && (attribStr.endsWith(","))) {
+//				attribStr = attribStr.substring(0, attribStr.length() - 1);
+//			}
+//			// build the mandatory filter, e.g.(mandatory:&lt;*company, local)
+//			filter.append("(" + Constants.MANDATORY_DIRECTIVE + ":" + "<*"
+//					+ attribStr + ")");
+//		}
+
+		// Prune (& off the front and ) off end
+		String filterString = filter.toString();
+		int openBraces = 0;
+		for (int i = 0; openBraces < 3; i++) {
+			i = filterString.indexOf('(', i);
+			if (i == -1) {
+				break;
+			} else {
+				openBraces++;
+			}
+		}
+		if (openBraces < 3 && filterString.length() > 2) {
+			filter.delete(0, 2);
+		} else {
+			filter.append(")");
+		}
+
+		String result = "";
+		if (realAttrib != false) {
+			result = filter.toString();
+		}
+		return result;
+	}
+
+	/**
+   * Generate a filter from a set of attributes. This filter will be suitable
+   * for presentation to OBR. This means that, due to the way OBR works, it will
+   * include a stanza of the form, (mandatory:<*mandatoryAttribute) Filter
+   * strings generated by this method will therefore tend to break the standard
+   * OSGi Filter class. The OBR stanza can be stripped out later if required.
+   *
+   * We may wish to consider relocating this method since VersionRange has its
+   * own top level class.
+   *
+   * @param type
+   * @param name
+   * @param attribs
+   * @return filter string
+   */
+  public static String generateFilter(String type, String name,
+      Map<String, Object> attribs) {
+    StringBuffer filter = new StringBuffer();
+    String result;
+    // shortcut for the simple case with no attribs.
+
+    if (attribs == null || attribs.isEmpty())
+      filter.append("(" + type + "=" + name + ")");
+    else {
+      // process all the attribs passed.
+      // find out whether there are attributes on the filter
+
+      filter.append("(&(" + type + "=" + name + ")");
+
+      String filterString = generateFilter(attribs);
+
+      int start = 0;
+      int end = filterString.length();
+      if (filterString.startsWith("(&")) {
+        start = 2;
+        end--;
+      }
+
+      if ("".equals(filterString)) {
+        filter.delete(0, 2);
+      } else {
+        filter.append(filterString, start, end);
+        filter.append(")");
+      }
+    }
+
+    result = filter.toString();
+
+    return result;
+  }
+
+  private static Map<String, String> parseFilterList(String filter) {
+
+    Map<String, String> result = new HashMap<String, String>();
+    Set<String> negatedVersions = new HashSet<String>();
+    Set<String> negatedBundleVersions = new HashSet<String>();
+
+    String lowerVersion = null;
+    String upperVersion = null;
+    String lowerBundleVersion = null;
+    String upperBundleVersion = null;
+
+    Matcher m = FILTER_ATTR.matcher(filter);
+    while (m.find()) {
+      boolean negation = m.group(1) != null;
+      String attr = m.group(2);
+      String op = m.group(3);
+      String value = m.group(4);
+
+      if (Constants.VERSION_ATTRIBUTE.equals(attr)) {
+        if (negation) {
+          negatedVersions.add(value);
+        } else {
+          if (GREATER_EQ_OP.equals(op))
+            lowerVersion = value;
+          else if (LESS_EQ_OP.equals(op))
+            upperVersion = value;
+          else
+            throw new IllegalArgumentException();
+        }
+      } else if (Constants.BUNDLE_VERSION_ATTRIBUTE.equals(attr)) {
+        // bundle-version is like version, but may be specified at the
+        // same time
+        // therefore we have similar code with separate variables
+        if (negation) {
+          negatedBundleVersions.add(value);
+        } else {
+          if (GREATER_EQ_OP.equals(op))
+            lowerBundleVersion = value;
+          else if (LESS_EQ_OP.equals(op))
+            upperBundleVersion = value;
+          else
+            throw new IllegalArgumentException();
+        }
+      } else {
+        result.put(attr, value);
+      }
+    }
+
+    if (lowerVersion != null) {
+      StringBuilder versionAttr = new StringBuilder(lowerVersion);
+      if (upperVersion != null) {
+        versionAttr.append(",").append(upperVersion).insert(0,
+            negatedVersions.contains(lowerVersion) ? '(' : '[').append(
+            negatedVersions.contains(upperVersion) ? ')' : ']');
+      }
+
+      result.put(Constants.VERSION_ATTRIBUTE, versionAttr.toString());
+    }
+    // Do it again for bundle-version
+    if (lowerBundleVersion != null) {
+      StringBuilder versionAttr = new StringBuilder(lowerBundleVersion);
+      if (upperBundleVersion != null) {
+        versionAttr.append(",").append(upperBundleVersion).insert(0,
+            negatedBundleVersions.contains(lowerBundleVersion) ? '(' : '[')
+            .append(
+                negatedBundleVersions.contains(upperBundleVersion) ? ')' : ']');
+      }
+
+      result.put(Constants.BUNDLE_VERSION_ATTRIBUTE, versionAttr.toString());
+    }
+
+    return result;
+  }
+
+  public static Map<String,String> parseFilter(String filter)
+  {
+    Map<String,String> result;
+    if (filter.startsWith("(&")) {
+      result = parseFilterList(filter.substring(2, filter.length()-1));
+    } else {
+      result = parseFilterList(filter);
+    }
+    return result;
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/1bcdb173/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderUtils.java
----------------------------------------------------------------------
diff --git a/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderUtils.java b/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderUtils.java
new file mode 100644
index 0000000..8c87616
--- /dev/null
+++ b/region/src/main/java/org/apache/karaf/region/persist/internal/util/ManifestHeaderUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.karaf.region.persist.internal.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ManifestHeaderUtils {
+
+     /**
+     *
+     * Splits a delimiter separated string, tolerating presence of non separator commas
+     * within double quoted segments.
+     *
+     * Eg.
+     * com.ibm.ws.eba.helloWorldService;version="[1.0.0, 1.0.0]" &
+     * com.ibm.ws.eba.helloWorldService;version="1.0.0"
+     * com.ibm.ws.eba.helloWorld;version="2";bundle-version="[2,30)"
+     * com.acme.foo;weirdAttr="one;two;three";weirdDir:="1;2;3"
+     *  @param value          the value to be split
+     *  @param delimiter      the delimiter string such as ',' etc.
+     *  @return List<String>  the components of the split String in a list
+     */
+    public static List<String> split(String value, String delimiter)
+    {
+      List<String> result = new ArrayList<String>();
+      if (value != null) {
+        String[] packages = value.split(delimiter);
+
+        for (int i = 0; i < packages.length; ) {
+          String tmp = packages[i++].trim();
+          // if there is a odd number of " in a string, we need to append
+          while (count(tmp, "\"") % 2 != 0) {
+            // check to see if we need to append the next package[i++]
+              if (i<packages.length)
+                tmp = tmp + delimiter + packages[i++].trim();
+              else
+                // oops. The double quotes are not paired up. We have reached to the end of the string.
+                throw new IllegalArgumentException("Unmatched double quotes: " + tmp);
+          }
+
+          result.add(tmp);
+
+        }
+      }
+      return result;
+    }
+
+    /**
+     * count the number of characters in a string
+     * @param parent The string to be searched
+     * @param subString The substring to be found
+     * @return the number of occurrence of the subString
+     */
+     private static int count(String parent, String subString) {
+
+       int count = 0 ;
+       int i = parent.indexOf(subString);
+       while (i > -1) {
+         if (parent.length() >= i+1)
+           parent = parent.substring(i+1);
+         count ++;
+         i = parent.indexOf(subString);
+       }
+       return count;
+     }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1bcdb173/region/src/main/java/org/apache/karaf/region/persist/internal/util/VersionRange.java
----------------------------------------------------------------------
diff --git a/region/src/main/java/org/apache/karaf/region/persist/internal/util/VersionRange.java b/region/src/main/java/org/apache/karaf/region/persist/internal/util/VersionRange.java
new file mode 100644
index 0000000..19bbc77
--- /dev/null
+++ b/region/src/main/java/org/apache/karaf/region/persist/internal/util/VersionRange.java
@@ -0,0 +1,456 @@
+/*
+ * 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.karaf.region.persist.internal.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Version;
+
+public final class VersionRange {
+
+    /** A string representation of the version. */
+    private String version;
+
+    /** The minimum desired version for the bundle */
+    private Version minimumVersion;
+
+    /** The maximum desired version for the bundle */
+    private Version maximumVersion;
+
+    /** True if the match is exclusive of the minimum version */
+    private boolean minimumExclusive;
+
+    /** True if the match is exclusive of the maximum version */
+    private boolean maximumExclusive;
+
+    /** A regexp to select the version */
+    private static final Pattern versionCapture = Pattern.compile("\"?(.*?)\"?$");
+
+    /**
+     *
+     * @param version
+     *            version for the verioninfo
+     */
+    public VersionRange(String version) {
+        this.version = version;
+        processVersionAttribute(version);
+    }
+
+    /**
+     * This method should be used to create a version range from a single
+     * version string.
+     * @param version
+     *            version for the versioninfo
+     * @param exactVersion
+     *            whether this is an exact version {@code true} or goes to infinity
+     *            {@code false}
+     */
+    public VersionRange(String version, boolean exactVersion) {
+
+        if (exactVersion) {
+            // Do not store this string as it might be just a version, or a range!
+            processExactVersionAttribute(version);
+        } else {
+            this.version = version;
+            processVersionAttribute(this.version);
+        }
+
+        assertInvariants();
+    }
+
+    /**
+     * Constructor designed for internal use only.
+     *
+     * @param maximumVersion
+     * @param maximumExclusive
+     * @param minimumVersion
+     * @param minimumExclusive
+     * @throws IllegalArgumentException
+     *             if parameters are not valid.
+     */
+    private VersionRange(Version maximumVersion,
+                         boolean maximumExclusive,
+                         Version minimumVersion,
+                         boolean minimumExclusive) {
+        this.maximumVersion = maximumVersion;
+        this.maximumExclusive = maximumExclusive;
+        this.minimumVersion = minimumVersion;
+        this.minimumExclusive = minimumExclusive;
+
+        assertInvariants();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.apache.aries.application.impl.VersionRange#toString()
+     */
+    @Override
+    public String toString() {
+        // Some constructors don't take in a string that we can return directly,
+        // so construct one if needed
+        if (version == null) {
+            if (maximumVersion == null) {
+                version = minimumVersion.toString();
+            } else {
+                version = (minimumExclusive ? "(" : "[") + minimumVersion + "," + maximumVersion
+                          + (maximumExclusive ? ")" : "]");
+            }
+        }
+        return this.version;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + minimumVersion.hashCode();
+        result = 31 * result + (minimumExclusive ? 1 : 0);
+        result = 31 * result + (maximumVersion != null ? maximumVersion.hashCode() : 0);
+        result = 31 * result + (maximumExclusive ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        boolean result = false;
+        if (this == other) {
+            result = true;
+        } else if (other instanceof VersionRange) {
+            VersionRange vr = (VersionRange) other;
+            result = minimumVersion.equals(vr.minimumVersion)
+                     && minimumExclusive == vr.minimumExclusive
+                     && (maximumVersion == null ? vr.maximumVersion == null : maximumVersion
+                             .equals(vr.maximumVersion)) && maximumExclusive == vr.maximumExclusive;
+        }
+
+        return result;
+    }
+
+    /**
+     * this method returns the exact version from the versionInfo obj.
+     * this is used for DeploymentContent only to return a valid exact version
+     * otherwise, null is returned.
+     * @return the exact version
+     */
+    public Version getExactVersion() {
+        Version v = null;
+        if (isExactVersion()) {
+            v = getMinimumVersion();
+        }
+        return v;
+    }
+
+    /**
+     * get the maximum version
+     * @return    the maximum version
+     */
+    public Version getMaximumVersion() {
+        return maximumVersion;
+    }
+
+    /**
+     * get the minimum version
+     * @return    the minimum version
+     */
+    public Version getMinimumVersion() {
+        return minimumVersion;
+    }
+
+    /**
+     * is the maximum version exclusive
+     * @return is the max version in the range.
+     */
+    public boolean isMaximumExclusive() {
+        return maximumExclusive;
+    }
+
+    /**
+     * is the maximum version unbounded
+     * @return true if no upper bound was specified.
+     */
+    public boolean isMaximumUnbounded() {
+        boolean unbounded = maximumVersion == null;
+        return unbounded;
+    }
+
+    /**
+     * is the minimum version exclusive
+     * @return true if the min version is in range.
+     */
+    public boolean isMinimumExclusive() {
+        return minimumExclusive;
+    }
+
+    /**
+     * this is designed for deployed-version as that is the exact version.
+     *
+     * @param version
+     * @return
+     * @throws IllegalArgumentException
+     */
+    private boolean processExactVersionAttribute(String version) throws IllegalArgumentException {
+        boolean success = processVersionAttribute(version);
+
+        if (maximumVersion == null) {
+            maximumVersion = minimumVersion;
+        }
+
+        if (!minimumVersion.equals(maximumVersion)) {
+            throw new IllegalArgumentException("Version is not exact: " + version);
+        }
+
+        if (!!!isExactVersion()) {
+            throw new IllegalArgumentException("Version is not exact: " + version);
+        }
+
+        return success;
+    }
+
+    /**
+     * process the version attribute,
+     *
+     * @param version
+     *            the value to be processed
+     * @return
+     * @throws IllegalArgumentException
+     */
+    private boolean processVersionAttribute(String version) throws IllegalArgumentException {
+        boolean success = false;
+
+        if (version == null) {
+            throw new IllegalArgumentException("Version is null");
+        }
+
+        Matcher matches = versionCapture.matcher(version);
+
+        if (matches.matches()) {
+            String versions = matches.group(1);
+
+            if ((versions.startsWith("[") || versions.startsWith("("))
+                && (versions.endsWith("]") || versions.endsWith(")"))) {
+                if (versions.startsWith("["))
+                    minimumExclusive = false;
+                else if (versions.startsWith("("))
+                    minimumExclusive = true;
+
+                if (versions.endsWith("]"))
+                    maximumExclusive = false;
+                else if (versions.endsWith(")"))
+                    maximumExclusive = true;
+
+                int index = versions.indexOf(',');
+                String minVersion = versions.substring(1, index);
+                String maxVersion = versions.substring(index + 1, versions.length() - 1);
+
+                try {
+                    minimumVersion = new Version(minVersion.trim());
+                    maximumVersion = new Version(maxVersion.trim());
+                    success = true;
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalArgumentException("Version cannot be decoded: " + version, nfe);
+                }
+            } else {
+                try {
+                    if (versions.trim().length() == 0)
+                        minimumVersion = new Version(0, 0, 0);
+                    else
+                        minimumVersion = new Version(versions.trim());
+                    success = true;
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalArgumentException("Version cannot be decoded: " + version, nfe);
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Version cannot be decoded: " + version);
+        }
+
+        return success;
+    }
+
+    /**
+     * Assert object invariants. Called by constructors to verify that arguments
+     * were valid.
+     *
+     * @throws IllegalArgumentException
+     *             if invariants are violated.
+     */
+    private void assertInvariants() {
+        if (minimumVersion == null
+            || !isRangeValid(minimumVersion, minimumExclusive, maximumVersion, maximumExclusive)) {
+            IllegalArgumentException e = new IllegalArgumentException();
+            throw e;
+        }
+    }
+
+    /**
+     * Check if the supplied parameters describe a valid version range.
+     *
+     * @param min
+     *            the minimum version.
+     * @param minExclusive
+     *            whether the minimum version is exclusive.
+     * @param max
+     *            the maximum version.
+     * @param maxExclusive
+     *            whether the maximum version is exclusive.
+     * @return true is the range is valid; otherwise false.
+     */
+    private boolean isRangeValid(Version min,
+                                 boolean minExclusive,
+                                 Version max,
+                                 boolean maxExclusive) {
+        boolean result;
+
+        // A null maximum version is unbounded so means that minimum is smaller
+        // than
+        // maximum.
+        int minMaxCompare = (max == null ? -1 : min.compareTo(max));
+        if (minMaxCompare > 0) {
+            // Minimum larger than maximum is invalid.
+            result = false;
+        } else if (minMaxCompare == 0 && (minExclusive || maxExclusive)) {
+            // If min and max are the same, and either are exclusive, no valid
+            // range
+            // exists.
+            result = false;
+        } else {
+            // Range is valid.
+            result = true;
+        }
+
+        return result;
+    }
+
+    /**
+     * This method checks that the provided version matches the desired version.
+     *
+     * @param version
+     *            the version.
+     * @return true if the version matches, false otherwise.
+     */
+    public boolean matches(Version version) {
+        boolean result;
+        if (this.getMaximumVersion() == null) {
+            result = this.getMinimumVersion().compareTo(version) <= 0;
+        } else {
+            int minN = this.isMinimumExclusive() ? 0 : 1;
+            int maxN = this.isMaximumExclusive() ? 0 : 1;
+
+            result = (this.getMinimumVersion().compareTo(version) < minN)
+                     && (version.compareTo(this.getMaximumVersion()) < maxN);
+        }
+        return result;
+    }
+
+    /**
+     * check if the versioninfo is the exact version
+     * @return true if the range will match 1 exact version.
+     */
+    public boolean isExactVersion() {
+        return minimumVersion.equals(maximumVersion) && minimumExclusive == maximumExclusive
+               && !!!minimumExclusive;
+    }
+
+    /**
+     * Create a new version range that is the intersection of {@code this} and the argument.
+     * In other words, the largest version range that lies within both {@code this} and
+     * the parameter.
+     * @param r a version range to be intersected with {@code this}.
+     * @return a new version range, or {@code null} if no intersection is possible.
+     */
+    public VersionRange intersect(VersionRange r) {
+        // Use the highest minimum version.
+        final Version newMinimumVersion;
+        final boolean newMinimumExclusive;
+        int minCompare = minimumVersion.compareTo(r.getMinimumVersion());
+        if (minCompare > 0) {
+            newMinimumVersion = minimumVersion;
+            newMinimumExclusive = minimumExclusive;
+        } else if (minCompare < 0) {
+            newMinimumVersion = r.getMinimumVersion();
+            newMinimumExclusive = r.isMinimumExclusive();
+        } else {
+            newMinimumVersion = minimumVersion;
+            newMinimumExclusive = (minimumExclusive || r.isMinimumExclusive());
+        }
+
+        // Use the lowest maximum version.
+        final Version newMaximumVersion;
+        final boolean newMaximumExclusive;
+        // null maximum version means unbounded, so the highest possible value.
+        if (maximumVersion == null) {
+            newMaximumVersion = r.getMaximumVersion();
+            newMaximumExclusive = r.isMaximumExclusive();
+        } else if (r.getMaximumVersion() == null) {
+            newMaximumVersion = maximumVersion;
+            newMaximumExclusive = maximumExclusive;
+        } else {
+            int maxCompare = maximumVersion.compareTo(r.getMaximumVersion());
+            if (maxCompare < 0) {
+                newMaximumVersion = maximumVersion;
+                newMaximumExclusive = maximumExclusive;
+            } else if (maxCompare > 0) {
+                newMaximumVersion = r.getMaximumVersion();
+                newMaximumExclusive = r.isMaximumExclusive();
+            } else {
+                newMaximumVersion = maximumVersion;
+                newMaximumExclusive = (maximumExclusive || r.isMaximumExclusive());
+            }
+        }
+
+        VersionRange result;
+        if (isRangeValid(newMinimumVersion, newMinimumExclusive, newMaximumVersion,
+                newMaximumExclusive)) {
+            result = new VersionRange(newMaximumVersion, newMaximumExclusive, newMinimumVersion,
+                    newMinimumExclusive);
+        } else {
+            result = null;
+        }
+        return result;
+    }
+
+    /**
+     * Parse a version range..
+     *
+     * @param s
+     * @return VersionRange object.
+     * @throws IllegalArgumentException
+     *             if the String could not be parsed as a VersionRange
+     */
+    public static VersionRange parseVersionRange(String s) throws IllegalArgumentException {
+        return new VersionRange(s);
+    }
+
+    /**
+     * Parse a version range and indicate if the version is an exact version
+     *
+     * @param s
+     * @param exactVersion
+     * @return VersionRange object.
+     * @throws IllegalArgumentException
+     *             if the String could not be parsed as a VersionRange
+     */
+    public static VersionRange parseVersionRange(String s, boolean exactVersion)
+            throws IllegalArgumentException {
+        return new VersionRange(s, exactVersion);
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1bcdb173/region/src/main/resources/org/apache/karaf/region/persist/region.xsd
----------------------------------------------------------------------
diff --git a/region/src/main/resources/org/apache/karaf/region/persist/region.xsd b/region/src/main/resources/org/apache/karaf/region/persist/region.xsd
new file mode 100644
index 0000000..8ca26e7
--- /dev/null
+++ b/region/src/main/resources/org/apache/karaf/region/persist/region.xsd
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!-- $Rev$ $Date$ -->
+
+<xsd:schema xmlns="http://karaf.apache.org/xmlns/region/v1.0.0"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        targetNamespace="http://karaf.apache.org/xmlns/region/v1.0.0"
+        elementFormDefault="qualified"
+        attributeFormDefault="unqualified">
+
+
+    <xsd:annotation>
+        <xsd:documentation>
+            Defines the configuration elements for Apache Karaf region xml configuration.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="regions" type="regionsType"/>
+
+
+    <xsd:complexType name="regionsType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Regions element
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence>
+            <xsd:element name="region" type="regionType" minOccurs="0" maxOccurs="unbounded"/>
+            <xsd:element name="filter" type="filterType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+    </xsd:complexType>
+
+    <xsd:complexType name="regionType">
+        <xsd:annotation>
+            <xsd:documentation>
+                Region element
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:sequence>
+            <xsd:element name="bundle" type="regionBundleType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+        <xsd:attribute name="name" type="xsd:string" use="required"/>
+    </xsd:complexType>
+
+
+    <xsd:complexType name="regionBundleType">
+        <xsd:sequence/>
+        <xsd:attribute name="id" type="xsd:long"/>
+        <xsd:attribute name="location" type="xsd:string"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="filterBundleType">
+        <xsd:sequence>
+            <xsd:element name="attribute" type="filterAttributeType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+        <xsd:attribute name="id" type="xsd:long"/>
+        <xsd:attribute name="symbolic-name" type="xsd:string"/>
+        <xsd:attribute name="version" type="xsd:string"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="filterType">
+        <xsd:sequence>
+            <xsd:element name="bundle" type="filterBundleType" minOccurs="0" maxOccurs="unbounded"/>
+            <xsd:element name="package" type="filterPackageType" minOccurs="0" maxOccurs="unbounded"/>
+            <xsd:element name="namespace" type="filterNamespaceType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+        <xsd:attribute name="from" type="xsd:string" use="required"/>
+        <xsd:attribute name="to" type="xsd:string" use="required"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="filterPackageType">
+        <xsd:sequence>
+            <xsd:element name="attribute" type="filterAttributeType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+        <xsd:attribute name="name" type="xsd:string" use="required"/>
+        <xsd:attribute name="version" type="xsd:string"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="filterAttributeType">
+        <xsd:sequence/>
+        <xsd:attribute name="name" type="xsd:string" use="required"/>
+        <xsd:attribute name="value" type="xsd:string" use="required"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="filterNamespaceType">
+        <xsd:sequence>
+            <xsd:element name="attribute" type="filterAttributeType" minOccurs="0" maxOccurs="unbounded"/>
+        </xsd:sequence>
+        <xsd:attribute name="name" type="xsd:string" use="required"/>
+    </xsd:complexType>
+</xsd:schema>