Return-Path: Delivered-To: apmail-incubator-aries-commits-archive@minotaur.apache.org Received: (qmail 3214 invoked from network); 10 Mar 2010 20:00:19 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 10 Mar 2010 20:00:19 -0000 Received: (qmail 94295 invoked by uid 500); 10 Mar 2010 19:59:48 -0000 Delivered-To: apmail-incubator-aries-commits-archive@incubator.apache.org Received: (qmail 94201 invoked by uid 500); 10 Mar 2010 19:59:48 -0000 Mailing-List: contact aries-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: aries-dev@incubator.apache.org Delivered-To: mailing list aries-commits@incubator.apache.org Received: (qmail 94193 invoked by uid 99); 10 Mar 2010 19:59:48 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Mar 2010 19:59:48 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Mar 2010 19:59:46 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 4B5E423889C5; Wed, 10 Mar 2010 19:59:26 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r921533 - /incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java Date: Wed, 10 Mar 2010 19:59:26 -0000 To: aries-commits@incubator.apache.org From: timothyjward@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100310195926.4B5E423889C5@eris.apache.org> Author: timothyjward Date: Wed Mar 10 19:59:25 2010 New Revision: 921533 URL: http://svn.apache.org/viewvc?rev=921533&view=rev Log: ARIES-239 : Improvements to ManifestHeaderProcessor Modified: incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java Modified: incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java URL: http://svn.apache.org/viewvc/incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java?rev=921533&r1=921532&r2=921533&view=diff ============================================================================== --- incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java (original) +++ incubator/aries/trunk/application/application-utils/src/main/java/org/apache/aries/application/utils/manifest/ManifestHeaderProcessor.java Wed Mar 10 19:59:25 2010 @@ -37,6 +37,9 @@ 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. @@ -430,125 +433,197 @@ public class ManifestHeaderProcessor } /** - * We may wish to consider moving this method to VersionRange. + * 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 attribs) { + StringBuilder filter = new StringBuilder("(&"); + boolean realAttrib = false; + StringBuffer realAttribs = new StringBuffer(); + + if (attribs == null) { + attribs = new HashMap(); + } + + for (Map.Entry 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()); + + 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().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:<*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 + * @return filter string */ - public static String generateFilter(String type, String name, Map attribs){ + public static String generateFilter(String type, String name, + Map attribs) { StringBuffer filter = new StringBuffer(); - StringBuffer realAttribs = new StringBuffer(); String result; - //shortcut for the simple case with no attribs. - boolean realAttrib = false; - if(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+")"); - for(Map.Entry 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()); + // shortcut for the simple case with no attribs. - 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().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(", "); - } - } - } - // tidy up realAttribs - remove the final , - - 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:<*company, local) - filter.append("(" + Constants.MANDATORY_DIRECTIVE + ":" + "<*"+attribStr + ")"); + 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--; } - filter.append(")"); - } - - if (!!!(realAttrib)) { - result = "("+type+"="+name+")"; - } else { - result = filter.toString(); + if ("".equals(filterString)) { + filter.delete(0, 2); + } else { + filter.append(filterString, start, end); + filter.append(")"); + } } - + + result = filter.toString(); + return result; } - private static final Pattern FILTER_ATTR = Pattern.compile("(\\(!)?\\((.*?)([<>]?=)(.*?)\\)\\)?"); - private static final String LESS_EQ_OP = "<="; - private static final String GREATER_EQ_OP = ">="; - - private static Map parseFilterList(String filter) - { + private static Map parseFilterList(String filter) { + Map result = new HashMap(); Set negatedVersions = new HashSet(); - + Set negatedBundleVersions = new HashSet(); + 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); @@ -560,6 +635,20 @@ public class ManifestHeaderProcessor 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); } @@ -568,17 +657,29 @@ public class ManifestHeaderProcessor if (lowerVersion != null) { StringBuilder versionAttr = new StringBuilder(lowerVersion); if (upperVersion != null) { - versionAttr.append(",") - .append(upperVersion) - .insert(0, negatedVersions.contains(lowerVersion) ? '(' : '[') - .append(negatedVersions.contains(upperVersion) ? ')' : ']'); + 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 parseFilter(String filter) { Map result;