Return-Path: X-Original-To: apmail-felix-commits-archive@www.apache.org Delivered-To: apmail-felix-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4673C97BA for ; Mon, 17 Oct 2011 10:32:41 +0000 (UTC) Received: (qmail 32866 invoked by uid 500); 17 Oct 2011 10:32:41 -0000 Delivered-To: apmail-felix-commits-archive@felix.apache.org Received: (qmail 32836 invoked by uid 500); 17 Oct 2011 10:32:41 -0000 Mailing-List: contact commits-help@felix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@felix.apache.org Delivered-To: mailing list commits@felix.apache.org Received: (qmail 32818 invoked by uid 99); 17 Oct 2011 10:32:41 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 17 Oct 2011 10:32:41 +0000 X-ASF-Spam-Status: No, hits=-1994.3 required=5.0 tests=ALL_TRUSTED,URIBL_DBL_SPAM,URIBL_RED,URIBL_SBL 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; Mon, 17 Oct 2011 10:32:26 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 61B7123889FA for ; Mon, 17 Oct 2011 10:32:03 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1185095 [4/15] - in /felix/trunk/bundleplugin: ./ src/main/java/aQute/ src/main/java/aQute/bnd/ src/main/java/aQute/bnd/annotation/ src/main/java/aQute/bnd/annotation/component/ src/main/java/aQute/bnd/annotation/metatype/ src/main/java/aQ... Date: Mon, 17 Oct 2011 10:31:51 -0000 To: commits@felix.apache.org From: mcculls@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20111017103203.61B7123889FA@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,481 @@ +package aQute.bnd.help; + +import java.util.*; +import java.util.regex.*; + +import aQute.lib.osgi.*; + +public class Syntax implements Constants { + final String header; + final String lead; + final String example; + final Pattern pattern; + final String values; + final Syntax[] children; + + static Syntax version = new Syntax( + VERSION_ATTRIBUTE, + "A version range to select the version of an export definition. The default value is 0.0.0 .", + "version=\"[1.2,3.0)\"", + null, + Verifier.VERSIONRANGE); + static Syntax bundle_symbolic_name = new Syntax( + BUNDLE_SYMBOLIC_NAME_ATTRIBUTE, + "The bundle symbolic name of the exporting bundle.", + "bundle-symbolic-name=com.acme.foo.daffy", + null, + Verifier.SYMBOLICNAME); + + static Syntax bundle_version = new Syntax( + BUNDLE_VERSION_ATTRIBUTE, + "a version range to select the bundle version of the exporting bundle. The default value is 0.0.0.", + "bundle-version=1.3", + null, + Verifier.VERSIONRANGE); + + static Syntax path_version = new Syntax( + VERSION_ATTRIBUTE, + "Specifies the range in the repository, project, or file", + "version=project", + "project,type", + Pattern + .compile("project|type|" + + Verifier.VERSIONRANGE + .toString())); + + @SuppressWarnings("deprecation") + static Syntax[] syntaxes = new Syntax[] { + new Syntax( + BUNDLE_ACTIVATIONPOLICY, + "The Bundle-ActivationPolicy specifies how the framework should activate the bundle once started. ", + "Bundle-ActivationPolicy: lazy", "lazy", Pattern + .compile("lazy")), + + new Syntax( + BUNDLE_ACTIVATOR, + "The Bundle-Activator header specifies the name of the class used to start and stop the bundle. ", + "Bundle-Activator: com.acme.foo.Activator", + "${classes;implementing;org.osgi.framework.BundleActivator}", + Verifier.FQNPATTERN), + new Syntax( + BUNDLE_CATEGORY, + "The Bundle-Category header holds a comma-separated list of category names", + "Bundle-Category: test", + "osgi,test,game,util,eclipse,netbeans,jdk,specification", + null), + new Syntax( + BUNDLE_CLASSPATH, + "The Bundle-ClassPath header defines a comma-separated list of JAR file path names or directories (inside the bundle) containing classes and resources. The period (’.’) specifies the root directory of the bundle’s JAR. The period is also the default.", + "Bundle-Classpath: /lib/libnewgen.so, .", null, + Verifier.PATHPATTERN), + new Syntax( + BUNDLE_CONTACTADDRESS, + "The Bundle-ContactAddress header provides the contact address of the vendor. ", + "Bundle-ContactAddress: 2400 Oswego Road, Austin, TX 74563", + null, null), + new Syntax( + BUNDLE_COPYRIGHT, + "The Bundle-Copyright header contains the copyright specification for this bundle. ", + "Bundle-Copyright: OSGi (c) 2002", null, null), + new Syntax( + BUNDLE_DESCRIPTION, + "The Bundle-Description header defines a short description of this bundle.", + "Bundle-Description: Ceci ce n'est pas une bundle", null, + null), + + new Syntax( + BUNDLE_DOCURL, + "The Bundle-DocURL headers must contain a URL pointing to documentation about this bundle.", + "Bundle-DocURL: http://www.aQute.biz/Code/Bnd", null, + Verifier.URLPATTERN), + + new Syntax( + BUNDLE_ICON, + "The optional Bundle-Icon header provides a list of (relative) URLs to icons representing this bundle in different sizes. ", + "Bundle-Icon: /icons/bnd.png;size=64", "/icons/bundle.png", + Verifier.URLPATTERN, new Syntax("size", + "Icons size in pixels, e.g. 64", "64", + "16,32,48,64,128", Verifier.NUMBERPATTERN)), + + new Syntax( + BUNDLE_LICENSE, + "The Bundle-License header provides an optional machine readable form of license information. The purpose of this header is to automate some of the license processing required by many organizations", + "Bundle License: http://www.opensource.org/licenses/jabberpl.php", + "http://www.apache.org/licenses/LICENSE-2.0,<>", + Pattern.compile("(" + Verifier.URLPATTERN + + "|<>)"), new Syntax( + DESCRIPTION_ATTRIBUTE, + "Human readable description of the license", + "description=\"Described the license here\"", null, + Verifier.ANYPATTERN), new Syntax(LINK_ATTRIBUTE, + "", "", null, Verifier.URLPATTERN)), + new Syntax( + BUNDLE_LOCALIZATION, + "The Bundle-Localization header contains the location in the bundle where localization files can be found. The default value is OSGI-INF/l10n/bundle. Translations are by default therefore OSGI-INF/l10n/bundle_de.properties, OSGI-INF/l10n/bundle_nl.properties, etc.", + "Bundle-Localization: OSGI-INF/l10n/bundle", + "OSGI-INF/l10n/bundle", Verifier.URLPATTERN), + new Syntax( + BUNDLE_MANIFESTVERSION, + "This header is set by bnd automatically to 2. The Bundle-ManifestVersion header defines that the bundle follows the rules of this specification. The Bundle-ManifestVersion header determines whether the bundle follows the rules of this specification.", + "# Bundle-ManifestVersion: 2", "2", Verifier.NUMBERPATTERN), + new Syntax( + BUNDLE_NAME, + "This header will be derived from the Bundle-SymbolicName if not set. The Bundle-Name header defines a readable name for this bundle. This should be a short, human-readable name that can contain spaces.", + "Bundle-Name: My Bundle", null, Verifier.ANYPATTERN), + new Syntax( + BUNDLE_NATIVECODE, + "The Bundle-NativeCode header contains a specification of native code libraries contained in this bundle. ", + "Bundle-NativeCode: /lib/http.DLL; osname = QNX; osversion = 3.1", + null, + Verifier.PATHPATTERN, + new Syntax(OSNAME_ATTRIBUTE, + "The name of the operating system", "osname=MacOS", + Processor.join(Verifier.OSNAMES, ","), + Verifier.ANYPATTERN), + new Syntax(OSVERSION_ATTRIBUTE, "Operating System Version", + "osversion=3.1", null, Verifier.ANYPATTERN), + new Syntax(LANGUAGE_ATTRIBUTE, "Language ISO 639 code", + "language=nl", null, Verifier.ISO639), + new Syntax(PROCESSOR_ATTRIBUTE, "Processor name", + "processor=x86", Processor.join( + Verifier.PROCESSORNAMES, ","), + Verifier.ANYPATTERN), + new Syntax( + SELECTION_FILTER_ATTRIBUTE, + "The value of this attribute must be a filter expression that indicates if the native code clause should be selected or not.", + "selection-filter=\"(com.acme.windowing=win32)\"", + null, Verifier.FILTERPATTERN)), + new Syntax( + BUNDLE_REQUIREDEXECUTIONENVIRONMENT, + "The Bundle-RequiredExecutionEnvironment contains a comma-separated list of execution environments that must be present on the Service Platform.", + "Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0", + Processor.join(Verifier.EES, ","), Verifier.ANYPATTERN), + + new Syntax( + BUNDLE_SYMBOLICNAME, + "The Bundle-SymbolicName header specifies a non-localizable name for this bundle. The bundle symbolic name together with a version must identify a unique bundle. The bundle symbolic name should be based on the reverse domain name convention", + "Bundle-SymbolicName: com.acme.foo.daffy;singleton:=true", + "${p}", + Verifier.SYMBOLICNAME, + new Syntax( + SINGLETON_DIRECTIVE, + " Indicates that the bundle can only have a single version resolved. A value of true indicates that the bundle is a singleton bundle. The default value is false. The Framework must resolve at most one bundle when multiple versions of a singleton bundle with the same symbolic name are installed. Singleton bundles do not affect the resolution of non-singleton bundles with the same symbolic name.", + "false", "true,false", Verifier.TRUEORFALSEPATTERN), + new Syntax( + FRAGMENT_ATTACHMENT_DIRECTIVE, + "Defines how fragments are allowed to be attached, see the fragments in Fragment Bundles on page73. The following values are valid for this directive:", + "", "always|never|resolve-time", Pattern + .compile("always|never|resolve-time")), + new Syntax(BLUEPRINT_WAIT_FOR_DEPENDENCIES_ATTRIBUTE, "", + "", "true,false", Verifier.TRUEORFALSEPATTERN), + new Syntax(BLUEPRINT_TIMEOUT_ATTRIBUTE, "", "", + "30000,60000,300000", Verifier.NUMBERPATTERN)), + + new Syntax( + BUNDLE_UPDATELOCATION, + "The Bundle-UpdateLocation header specifies a URL where an update for this bundle should come from. If the bundle is updated, this location should be used, if present, to retrieve the updated JAR file.", + "Bundle-UpdateLocation: http://www.acme.com/Firewall/bundle.jar", + null, Verifier.URLPATTERN), + + new Syntax( + BUNDLE_VENDOR, + "The Bundle-Vendor header contains a human-readable description of the bundle vendor. ", + "Bundle-Vendor: OSGi Alliance ", null, null), + + new Syntax( + BUNDLE_VERSION, + "The Bundle-Version header specifies the version of this bundle", + "Bundle-Version: 1.23.4.build200903221000", null, + Verifier.VERSION), + + new Syntax( + DYNAMICIMPORT_PACKAGE, + "The DynamicImport-Package header contains a comma-separated list of package names that should be dynamically imported when needed.", + "DynamicImport-Package: com.acme.plugin.*", "", + Verifier.WILDCARDNAMEPATTERN, version, + bundle_symbolic_name, bundle_version), + + new Syntax( + EXPORT_PACKAGE, + "The Export-Package header contains a declaration of exported packages.", + "Export-Package: org.osgi.util.tracker;version=1.3", + "${packages}", + null, + new Syntax( + NO_IMPORT_DIRECTIVE, + "By default, bnd makes all exports also imports. Adding a -noimport to an exported package will make it export only", + "-noimport:=true", "true,false", + Verifier.TRUEORFALSEPATTERN), + new Syntax( + USES_DIRECTIVE, + "Calculated by bnd: It is a comma-separated list of package names that are used by the exported package", + "Is calculated by bnd", null, null), + new Syntax( + MANDATORY_DIRECTIVE, + "A comma-separated list of attribute names. Note that the use of a comma in the value requires it to be enclosed in double quotes. A bundle importing the package must specify the mandatory attributes, with a value that matches, to resolve to the exported package", + "mandatory=\"bar,foo\"", null, null), + new Syntax( + INCLUDE_DIRECTIVE, + "A comma-separated list of class names that must be visible to an importer", + "include:=\"Qux*\"", null, null), + new Syntax( + EXCLUDE_DIRECTIVE, + "A comma-separated list of class names that must not be visible to an importer", + "exclude:=\"QuxImpl*,BarImpl\"", null, + Verifier.WILDCARDNAMEPATTERN), new Syntax( + IMPORT_DIRECTIVE, "Experimental", "", null, null) + + ), + new Syntax(EXPORT_SERVICE, "Deprecated", + "Export-Service: org.osgi.service.log.LogService ", + "${classes;implementing;*}", null), + new Syntax( + FRAGMENT_HOST, + "The Fragment-Host header defines the host bundle for this fragment.", + "Fragment-Host: org.eclipse.swt; bundle-version=\"[3.0.0,4.0.0)\"", + null, + null, + new Syntax( + EXTENSION_DIRECTIVE, + " Indicates this extension is a system or boot class path extension. It is only applicable when the Fragment-Host is the System Bundle", + "extension:=framework", "framework,bootclasspath", + Pattern.compile("framework|bootclasspath")), + bundle_version), + new Syntax( + IMPORT_PACKAGE, + "This header is normally calculated by bnd, however, you can decorate packages or skip packages. The Import-Package header declares the imported packages for this bundle", + "Import-Package: !com.exotic.*, com.acme.foo;vendor=ACME, *", + "${exported_packages}", + Verifier.WILDCARDNAMEPATTERN, + new Syntax( + REMOVE_ATTRIBUTE_DIRECTIVE, + "Remove the given attributes from matching imported packages", + "-remove-attribute:=foo.*", null, + Verifier.WILDCARDNAMEPATTERN), + new Syntax( + RESOLUTION_DIRECTIVE, + "Indicates that the packages must be resolved if the value is mandatory, which is the default. If mandatory packages cannot be resolved, then the bundle must fail to resolve. A value of optional indicates that the packages are optional", + "resolution:=optional", "mandatory,optional", + Pattern.compile("mandatory|optional") + + ), version, bundle_symbolic_name, bundle_version), + + new Syntax( + REQUIRE_BUNDLE, + "The Require-Bundle header specifies the required exports from another bundle.", + "Require-Bundle: com.acme.chess", + null, + Verifier.WILDCARDNAMEPATTERN, + + new Syntax( + VISIBILITY_DIRECTIVE, + " If the value is private (Default), then all visible packages from the required bundles are not re-exported. If the value is reexport then bundles that require this bundle will transitively have access to these required bundle’s exported packages.", + "visibility:=private", "private,reexport", Pattern + .compile("private|reexport")), + + new Syntax( + RESOLUTION_DIRECTIVE, + "If the value is mandatory (default) then the required bundle must exist for this bundle to resolve. If the value is optional, the bundle will resolve even if the required bundle does not exist.", + "resolution:=optional", "mandatory,optional", + Pattern.compile("mandatory|optional")), + + new Syntax( + SPLIT_PACKAGE_DIRECTIVE, + "Indicates how an imported package should be merged when it is split between different exporters. The default is merge-first with warning", + "-split-package:=merge-first", + "merge-first,merge-last,error,first", + Pattern + .compile("merge-first|merge-last|error|first")), + bundle_version + + ), + new Syntax( + BUILDPATH, + "Provides the class path for building the jar. The entries are references to the repository", + "-buildpath=osgi;version=4.1", "${repo;bsns}", + Verifier.SYMBOLICNAME, path_version), + new Syntax( + BUMPPOLICY, + "Sets the version bump policy. This is a parameter to the ${version} macro.", + "-bumppolicy==+0", "==+,=+0,+00", Pattern + .compile("[=+-0][=+-0][=+-0]")), + + new Syntax( + CONDUIT, + "Allows a bnd file to point to files which will be returned when the bnd file is build", + "-conduit= jar/osgi.jar", null, null), + + new Syntax( + DEPENDSON, + "List of project names that this project directly depends on. These projects are always build ahead of this project", + "-dependson=org.acme.cm", "${projects}", null), + + new Syntax(DEPLOYREPO, + "Specifies to which repo the project should be deployed.", + "-deployrepo=cnf", "${repos}", null), + + new Syntax( + DONOTCOPY, + "Regular expression for names of files and directories that should not be copied when discovered", + "-donotcopy=(CVS|\\.svn)", null, null), + + new Syntax( + EXPORT_CONTENTS, + "Build the JAR in the normal way but use this header for the Export-Package header manifest generation, same format", + "-exportcontents=!*impl*,*;version=3.0", null, null), + + new Syntax( + FAIL_OK, + "Return with an ok status (0) even if the build generates errors", + "-failok=true", "true,false", Verifier.TRUEORFALSEPATTERN), + + new Syntax( + INCLUDE, + "Include files. If an entry starts with '-', it does not have to exist. If it starts with '~', it must not overwrite any existing properties", + "-include: -${java.user}/.bnd", null, null), + + new Syntax( + INCLUDERESOURCE, + "Include resources from the file system. You can specify a directory, or file. All files are copied to the root, unless a destination directory is indicated", + "-includeresource: lib=jar", null, null), + + new Syntax( + MAKE, + "Set patterns for make plugins. These patterns are used to find a plugin that can make a resource that can not be found.", + "-make: (*).jar;type=bnd; recipe=\"bnd/$1.bnd\"", null, + null, new Syntax("type", "Type name for plugin", + "type=bnd", "bnd", null), new Syntax("recipe", + "Recipe for the plugin, can use back references", + "recipe=\"bnd/$1.bnd\"", "bnd", null)), + + new Syntax( + MANIFEST, + "Directly include a manifest, do not use the calculated manifest", + "-manifest = META-INF/MANIFEST.MF", null, null), + + new Syntax(NOEXTRAHEADERS, "Do not generate housekeeping headers", + "-noextraheaders", "true,false", + Verifier.TRUEORFALSEPATTERN), + + new Syntax(NOUSES, + "Do not calculate the uses: directive on exports", + "-nouses=true", "true,false", Verifier.TRUEORFALSEPATTERN), + + new Syntax(NOPE, + "Deprecated, use -nobundles. ", + "-nope=true", "true,false", Verifier.TRUEORFALSEPATTERN), + + new Syntax( + PEDANTIC, + "Warn about things that are not really wrong but still not right", + "-nope=true", "true,false", Verifier.TRUEORFALSEPATTERN), + + new Syntax( + PLUGIN, + "Define the plugins", + "-plugin=aQute.lib.spring.SpringComponent,aQute.lib.deployer.FileRepo;location=${repo}", + null, null), + + new Syntax(SERVICE_COMPONENT, + "The header for Declarative Services", + "Service-Component=com.acme.Foo?;activate='start'", null, + null), + + new Syntax(POM, "Generate a maven pom", "-pom=true", "true,false", + Verifier.TRUEORFALSEPATTERN), + + new Syntax(RELEASEREPO, + "Specifies to which repo the project should be released.", + "-releaserepo=cnf", "${repos}", null), + + new Syntax(REMOVEHEADERS, + "Remove all headers that match the regular expressions", + "-removeheaders=FOO_.*,Proprietary", null, null), + new Syntax( + RESOURCEONLY, + "Normally bnd warns when the JAR does not contain any classes, this option suppresses this warning", + "-resourceonly=true", "true,false", + Verifier.TRUEORFALSEPATTERN), + new Syntax(SOURCES, "Include sources in the jar", "-sources=true", + "true,false", Verifier.TRUEORFALSEPATTERN), + new Syntax( + SOURCEPATH, + "List of directory names that used to source sources for -sources", + "-sourcepath:= src, test", null, null), + new Syntax( + SUB, + "Build a set of bnd files that use this bnd file as a basis. The list of bnd file can be specified with wildcards", + "-sub=com.acme.*.bnd", null, null), + new Syntax( + RUNPROPERTIES, + "Properties that are set as system properties before the framework is started", + "-runproperties= foo=3, bar=4", null, null), + new Syntax(RUNSYSTEMPACKAGES, + "Add additional system packages to a framework run", + "-runsystempackages=com.acme.foo,javax.management", null, + null), + new Syntax( + RUNBUNDLES, + "Add additional bundles, specified with their bsn and version like in -buildpath, that are started before the project is run", + "-runbundles=osgi;version=\"[4.1,4.2)\", junit.junit, com.acme.foo;version=project", + null, Verifier.SYMBOLICNAME, path_version), + new Syntax( + RUNPATH, + "Additional JARs for the VM path, should include the framework", + "-runpath=org.eclipse.osgi;version=3.5", null, null, + path_version), + new Syntax( + RUNVM, + "Additional arguments for the VM invokation. Keys that start with a - are added as options, otherwise they are treated as -D properties for the VM", + "-runvm=-Xmax=30", null, null), + new Syntax( + VERSIONPOLICY, + "Provides a version policy to imports that are calculated from exports", + "-versionpolicy = \"[${version;==;${@}},${version;+;${@}})\"", + null, null) + + }; + + public final static Map HELP = new HashMap(); + + static { + for (Syntax s : syntaxes) { + HELP.put(s.header, s); + } + } + + public Syntax(String header, String lead, String example, String values, + Pattern pattern, Syntax... children) { + this.header = header; + this.children = children; + this.lead = lead; + this.example = example; + this.values = values; + this.pattern = pattern; + } + + public String getLead() { + return lead; + } + + public String getExample() { + return example; + } + + public String getValues() { + return values; + } + + public String getPattern() { + return lead; + } + + public Syntax[] getChildren() { + return children; + } + + public String getHeader() { + return header; + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Syntax.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,5 @@ +package aQute.bnd.help; + +public interface Warnings { + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/Warnings.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/packageinfo URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/packageinfo?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/packageinfo (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/help/packageinfo Mon Oct 17 10:31:43 2011 @@ -0,0 +1 @@ +version 1.0 \ No newline at end of file Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,99 @@ +package aQute.bnd.make; + +import java.util.*; +import java.util.regex.*; + +import aQute.bnd.service.*; +import aQute.lib.osgi.*; + +public class Make { + Builder builder; + Map> make; + + public Make(Builder builder) { + this.builder = builder; + } + + public Resource process(String source) { + Map> make = getMakeHeader(); + builder.trace("make " + source); + + for (Map.Entry> entry : make + .entrySet()) { + Instruction instr = entry.getKey(); + Matcher m = instr.getMatcher(source); + if (m.matches() || instr.isNegated()) { + Map arguments = replace(m, entry.getValue()); + List plugins = builder.getPlugins(MakePlugin.class); + for (MakePlugin plugin : plugins) { + try { + Resource resource = plugin.make(builder, + source, arguments); + if (resource != null) { + builder.trace("Made " + source + " from args " + + arguments + " with " + plugin); + return resource; + } + } catch (Exception e) { + builder.error("Plugin " + plugin + + " generates error when use in making " + + source + " with args " + arguments, e); + } + } + } + } + return null; + } + + private Map replace(Matcher m, Map value) { + Map newArgs = Processor.newMap(); + for (Map.Entry entry : value.entrySet()) { + String s = entry.getValue(); + s = replace(m, s); + newArgs.put(entry.getKey(), s); + } + return newArgs; + } + + String replace(Matcher m, CharSequence s) { + StringBuffer sb = new StringBuffer(); + int max = '0' + m.groupCount() + 1; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '$' && i < s.length() - 1) { + c = s.charAt(++i); + if (c >= '0' && c <= max) { + int index = c - '0'; + String replacement = m.group(index); + if (replacement != null) + sb.append(replacement); + } else { + if (c == '$') + i++; + sb.append(c); + } + } else + sb.append(c); + } + return sb.toString(); + } + + Map> getMakeHeader() { + if (make != null) + return make; + make = Processor.newMap(); + + String s = builder.getProperty(Builder.MAKE); + Map> make = builder.parseHeader(s); + for (Iterator>> e = make + .entrySet().iterator(); e.hasNext();) { + Map.Entry> entry = e.next(); + String pattern = Processor.removeDuplicateMarker(entry.getKey()); + + Instruction instr = Instruction.getPattern(pattern); + this.make.put(instr, entry.getValue()); + } + + return this.make; + } +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/Make.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,65 @@ +package aQute.bnd.make; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import aQute.bnd.build.*; +import aQute.bnd.service.*; +import aQute.lib.osgi.*; + +public class MakeBnd implements MakePlugin, Constants { + final static Pattern JARFILE = Pattern.compile("(.+)\\.(jar|ipa)"); + + public Resource make(Builder builder, String destination, + Map argumentsOnMake) throws Exception { + String type = (String) argumentsOnMake.get("type"); + if (!"bnd".equals(type)) + return null; + + String recipe = (String) argumentsOnMake.get("recipe"); + if (recipe == null) { + builder.error("No recipe specified on a make instruction for " + + destination); + return null; + } + File bndfile = builder.getFile(recipe); + if (bndfile.isFile()) { + // We do not use a parent because then we would + // build ourselves again. So we can not blindly + // inherit the properties. + Builder bchild = builder.getSubBuilder(); + bchild.removeBundleSpecificHeaders(); + + // We must make sure that we do not include ourselves again! + bchild.setProperty(Analyzer.INCLUDE_RESOURCE, ""); + bchild.setProperty(Analyzer.INCLUDERESOURCE, ""); + bchild.setProperties(bndfile, builder.getBase()); + + Jar jar = bchild.build(); + Jar dot = builder.getTarget(); + + if (builder.hasSources()) { + for (String key : jar.getResources().keySet()) { + if (key.startsWith("OSGI-OPT/src")) + dot.putResource(key, (Resource) jar.getResource(key)); + } + } + builder.getInfo(bchild, bndfile.getName() +": "); + String debug = bchild.getProperty(DEBUG); + if (Processor.isTrue(debug)) { + if ( builder instanceof ProjectBuilder ) { + ProjectBuilder pb = (ProjectBuilder) builder; + File target = pb.getProject().getTarget(); + String bsn = bchild.getBsn(); + File output = new File(target, bsn+".jar"); + jar.write(output); + pb.getProject().getWorkspace().changedFile(output); + } + } + return new JarResource(jar); + } else + return null; + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeBnd.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,45 @@ +package aQute.bnd.make; + +import java.io.*; +import java.net.*; +import java.util.*; + +import aQute.bnd.service.*; +import aQute.lib.osgi.*; + +public class MakeCopy implements MakePlugin { + + public Resource make(Builder builder, String destination, + Map argumentsOnMake) throws Exception { + String type = argumentsOnMake.get("type"); + if (!type.equals("copy")) + return null; + + String from = argumentsOnMake.get("from"); + if (from == null) { + String content = argumentsOnMake.get("content"); + if (content == null) + throw new IllegalArgumentException( + "No 'from' or 'content' field in copy " + + argumentsOnMake); + return new EmbeddedResource(content.getBytes("UTF-8"),0); + } else { + + File f = builder.getFile(from); + if (f.isFile()) + return new FileResource(f); + else { + try { + URL url = new URL(from); + return new URLResource(url); + } catch(MalformedURLException mfue) { + // We ignore this + } + throw new IllegalArgumentException( + "Copy source does not exist " + from + + " for destination " + destination); + } + } + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/MakeCopy.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,165 @@ +package aQute.bnd.make.calltree; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import aQute.lib.osgi.*; + +/** + * Create an XML call tree of a set of classes. The structure of the XML is: + * + *
+ *    calltree ::= <using> <usedby>
+ *    using    ::= <method> *
+ *    usedby   ::= <method> *
+ *    method   ::= <ref> 
+ * 
+ * + * The using element contains methods in the set of classes and + * their references. The usedby element contains the used methods + * and their references to the set of classes. The ref element + * contains the class, the method name, the descriptor, and a pretty print + * version of the method. + * + * The XML does not contain an XML processor instruction to make it easier to + * include in other XML. The encoding is always UTF-8. + * + * This class can be used as a resource, just add it to a JAR and the data is + * generated when the resource is written (saving time when the JAR is up to + * date and does not have to be generated). However, the actual write method is + * a static method and can be called as well: + * {@link #writeCalltree(PrintWriter, Collection)}. + */ +public class CalltreeResource extends WriteResource { + Collection classes; + + /** + * Create a resource for inclusion that will print a call tree. + * + * @param values the classes for which the call tree is generated. + */ + public CalltreeResource(Collection values) { + this.classes = values; + System.out.println(values); + } + + /** + * We set the last modified to 0 so this resource does not force + * a new JAR if all other resources are up to date. + */ + public long lastModified() { + return 0; + } + + /** + * The write method is called to write the resource. We just call the static + * method. + */ + public void write(OutputStream out) throws Exception { + OutputStreamWriter osw = new OutputStreamWriter(out, Constants.DEFAULT_CHARSET); + PrintWriter pw = new PrintWriter(osw); + try { + writeCalltree(pw, classes); + } finally { + pw.flush(); + } + } + + /** + * Print the call tree in XML. + * + * @param out The output writer + * @param classes The set of classes + * @throws IOException Any errors + */ + public static void writeCalltree(PrintWriter out, Collection classes) + throws Exception { + + final Map> using = new TreeMap>(); + final Map> usedby = new TreeMap>(); + + ClassDataCollector cd = new ClassDataCollector() { + Clazz.MethodDef source; + + // Before a method is parsed + public void method(Clazz.MethodDef source) { + this.source = source; + xref(using, source, null); + xref(usedby, source, null); + } + + // For any reference in the previous method. + public void reference(Clazz.MethodDef reference) { + xref(using, source, reference); + xref(usedby, reference, source); + } + }; + for (Clazz clazz : classes) { + clazz.parseClassFileWithCollector(cd); + } + + out.println(""); + xref(out, "using", using); + xref(out, "usedby", usedby); + out.println(""); + } + + /* + * Add a new reference + */ + private static void xref( + Map> references, + Clazz.MethodDef source, Clazz.MethodDef reference) { + Set set = references.get(source); + if (set == null) + references.put(source, set=new TreeSet()); + if ( reference != null) + set.add(reference); + } + + /* + * Print out either using or usedby sets + */ + private static void xref(PrintWriter out, String group, + Map> references) { + out.println(" <" + group + ">"); + for (Map.Entry> entry : references + .entrySet()) { + Clazz.MethodDef source = entry.getKey(); + Set refs = entry.getValue(); + method(out, "method", source, ">"); + for (Clazz.MethodDef ref : refs) { + method(out, "ref", ref, "/>"); + } + out.println(" "); + } + out.println(" "); + } + + /* + * Print out a method. + */ + private static void method(PrintWriter out, String element, + Clazz.MethodDef source, String closeElement) { + out.println(" <" + element + " class='" + source.clazz + "'" + + getAccess(source.access) + + ( source.isConstructor() ? "" : " name='" + source.name + "'") + " descriptor='" + source.descriptor + "' pretty='" + + source.getPretty() + "'" + closeElement); + } + + private static String getAccess(int access) { + StringBuilder sb = new StringBuilder(); + if ( Modifier.isPublic(access) ) + sb.append(" public='true'"); + if ( Modifier.isStatic(access) ) + sb.append(" static='true'"); + if ( Modifier.isProtected(access) ) + sb.append(" protected='true'"); + if ( Modifier.isInterface(access) ) + sb.append(" interface='true'"); + + return sb.toString(); + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/calltree/CalltreeResource.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,343 @@ +package aQute.bnd.make.component; + +import static aQute.bnd.make.component.ServiceComponent.*; + +import java.lang.reflect.*; +import java.util.*; +import java.util.regex.*; + +import aQute.bnd.annotation.component.*; +import aQute.lib.osgi.*; +import aQute.libg.reporter.*; + +public class ComponentAnnotationReader extends ClassDataCollector { + String EMPTY[] = new String[0]; + private static final String V1_1 = "1.1.0"; // "1.1.0" + static Pattern BINDDESCRIPTOR = Pattern + .compile("\\(L([^;]*);(Ljava/util/Map;|Lorg/osgi/framework/ServiceReference;)*\\)V"); + static Pattern BINDMETHOD = Pattern.compile("(set|bind|add)(.)(.*)"); + + static Pattern ACTIVATEDESCRIPTOR = Pattern + .compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)V"); + static Pattern OLDACTIVATEDESCRIPTOR = Pattern + .compile("\\(Lorg/osgi/service/component/ComponentContext;\\)V"); + static Pattern OLDBINDDESCRIPTOR = Pattern.compile("\\(L([^;]*);\\)V"); + static Pattern REFERENCEBINDDESCRIPTOR = Pattern + .compile("\\(Lorg/osgi/framework/ServiceReference;\\)V"); + + Reporter reporter = new Processor(); + String method; + String methodDescriptor; + int methodAccess; + String className; + Clazz clazz; + String interfaces[]; + Set multiple = new HashSet(); + Set optional = new HashSet(); + Set dynamic = new HashSet(); + + Map map = new TreeMap(); + Set descriptors = new HashSet(); + List properties = new ArrayList(); + String version = null; + + // TODO make patterns for descriptors + + ComponentAnnotationReader(Clazz clazz) { + this.clazz = clazz; + } + + public void setReporter(Reporter reporter) { + this.reporter = reporter; + } + + public Reporter getReporter() { + return this.reporter; + } + + public static Map getDefinition(Clazz c) throws Exception { + return getDefinition(c, new Processor()); + } + + public static Map getDefinition(Clazz c, Reporter reporter) throws Exception { + ComponentAnnotationReader r = new ComponentAnnotationReader(c); + r.setReporter(reporter); + c.parseClassFileWithCollector(r); + r.finish(); + return r.map; + } + + public void annotation(Annotation annotation) { + + if (annotation.getName().equals(Component.RNAME)) { + set(COMPONENT_NAME, annotation.get(Component.NAME), "<>"); + set(COMPONENT_FACTORY, annotation.get(Component.FACTORY), false); + setBoolean(COMPONENT_ENABLED, annotation.get(Component.ENABLED), true); + setBoolean(COMPONENT_IMMEDIATE, annotation.get(Component.IMMEDIATE), false); + setBoolean(COMPONENT_SERVICEFACTORY, annotation.get(Component.SERVICEFACTORY), false); + + if (annotation.get(Component.DESIGNATE) != null) { + String configs = annotation.get(Component.DESIGNATE); + if (configs != null) { + set(COMPONENT_DESIGNATE, Clazz.objectDescriptorToFQN(configs), ""); + } + } + + if (annotation.get(Component.DESIGNATE_FACTORY) != null) { + String configs = annotation.get(Component.DESIGNATE_FACTORY); + if (configs != null) { + set(COMPONENT_DESIGNATEFACTORY, Clazz.objectDescriptorToFQN(configs), ""); + } + } + + setVersion((String) annotation.get(Component.VERSION)); + + String configurationPolicy = annotation.get(Component.CONFIGURATION_POLICY); + if (configurationPolicy != null) + set(COMPONENT_CONFIGURATION_POLICY, configurationPolicy.toLowerCase(), ""); + + doProperties(annotation); + + Object[] provides = (Object[]) annotation.get(Component.PROVIDE); + String[] p; + if (provides == null) { + // Use the found interfaces, but convert from internal to + // fqn. + if (interfaces != null) { + List result = new ArrayList(); + for (int i = 0; i < interfaces.length; i++) { + if (!interfaces[i].equals("scala/ScalaObject") ) + result.add(Clazz.internalToFqn(interfaces[i])); + } + p = result.toArray(EMPTY); + } else + p = EMPTY; + } else { + // We have explicit interfaces set + p = new String[provides.length]; + for (int i = 0; i < provides.length; i++) { + p[i] = descriptorToFQN(provides[i].toString()); + } + } + if (p.length > 0) { + set(COMPONENT_PROVIDE, Processor.join(Arrays.asList(p)), "<>"); + } + + } else if (annotation.getName().equals(Activate.RNAME)) { + if (!checkMethod()) + setVersion(V1_1); + + if (!ACTIVATEDESCRIPTOR.matcher(methodDescriptor).matches()) + reporter.error( + "Activate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s", + className, methodDescriptor); + + if (method.equals("activate") + && OLDACTIVATEDESCRIPTOR.matcher(methodDescriptor).matches()) { + // this is the default! + } else { + setVersion(V1_1); + set(COMPONENT_ACTIVATE, method, "<>"); + } + + } else if (annotation.getName().equals(Deactivate.RNAME)) { + if (!checkMethod()) + setVersion(V1_1); + + if (!ACTIVATEDESCRIPTOR.matcher(methodDescriptor).matches()) + reporter.error( + "Deactivate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s", + className, methodDescriptor); + if (method.equals("deactivate") + && OLDACTIVATEDESCRIPTOR.matcher(methodDescriptor).matches()) { + // This is the default! + } else { + setVersion(V1_1); + set(COMPONENT_DEACTIVATE, method, "<>"); + } + } else if (annotation.getName().equals(Modified.RNAME)) { + set(COMPONENT_MODIFIED, method, "<>"); + setVersion(V1_1); + } else if (annotation.getName().equals(Reference.RNAME)) { + + String name = (String) annotation.get(Reference.NAME); + String bind = method; + String unbind = null; + + if (name == null) { + Matcher m = BINDMETHOD.matcher(method); + if (m.matches()) { + name = m.group(2).toLowerCase() + m.group(3); + } else { + name = method.toLowerCase(); + } + } + String simpleName = name; + + unbind = annotation.get(Reference.UNBIND); + + if (bind != null) { + name = name + "/" + bind; + if (unbind != null) + name = name + "/" + unbind; + } + String service = annotation.get(Reference.SERVICE); + + if (service != null) { + service = Clazz.objectDescriptorToFQN(service); + } else { + // We have to find the type of the current method to + // link it to the referenced service. + Matcher m = BINDDESCRIPTOR.matcher(methodDescriptor); + if (m.matches()) { + service = Clazz.internalToFqn(m.group(1)); + } else + throw new IllegalArgumentException( + "Cannot detect the type of a Component Reference from the descriptor: " + + methodDescriptor); + } + + // Check if we have a target, this must be a filter + String target = annotation.get(Reference.TARGET); + if (target != null) { + Verifier.verifyFilter(target, 0); + service = service + target; + } + + Integer c = annotation.get(Reference.TYPE); + if (c != null && !c.equals(0) && !c.equals((int) '1')) { + service = service + (char) c.intValue(); + } + + if (map.containsKey(name)) + reporter.error( + "In component %s, Multiple references with the same name: %s. Previous def: %s, this def: %s", + name, map.get(name), service, ""); + map.put(name, service); + + if (isTrue(annotation.get(Reference.MULTIPLE))) + multiple.add(simpleName); + if (isTrue(annotation.get(Reference.OPTIONAL))) + optional.add(simpleName); + if (isTrue(annotation.get(Reference.DYNAMIC))) + dynamic.add(simpleName); + + if (!checkMethod()) + setVersion(V1_1); + else if (REFERENCEBINDDESCRIPTOR.matcher(methodDescriptor).matches() + || !OLDBINDDESCRIPTOR.matcher(methodDescriptor).matches()) + setVersion(V1_1); + } + } + + private void setVersion(String v) { + if (v == null) + return; + + if (version == null) + version = v; + else if (v.compareTo(version) > 0) // we're safe to 9.9.9 + version = v; + } + + private boolean checkMethod() { + return Modifier.isPublic(methodAccess) || Modifier.isProtected(methodAccess); + } + + static Pattern PROPERTY_PATTERN = Pattern.compile("[^=]+=.+"); + + private void doProperties(Annotation annotation) { + Object[] properties = annotation.get(Component.PROPERTIES); + + if (properties != null) { + for (Object o : properties) { + String p = (String) o; + if (PROPERTY_PATTERN.matcher(p).matches()) + this.properties.add(p); + else + throw new IllegalArgumentException("Malformed property '" + p + "' on: " + + annotation.get(Component.NAME)); + } + } + } + + private boolean isTrue(Object object) { + if (object == null) + return false; + return (Boolean) object; + } + + private void setBoolean(String string, Object object, boolean b) { + if (object == null) + object = b; + + Boolean bb = (Boolean) object; + if (bb == b) + return; + + map.put(string, bb.toString()); + } + + private void set(String string, Object object, Object deflt) { + if (object == null || object.equals(deflt)) + return; + + map.put(string, object.toString()); + } + + /** + * Skip L and ; and replace / for . in an object descriptor. + * + * A string like Lcom/acme/Foo; becomes com.acme.Foo + * + * @param string + * @return + */ + + private String descriptorToFQN(String string) { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < string.length() - 1; i++) { + char c = string.charAt(i); + if (c == '/') + c = '.'; + sb.append(c); + } + return sb.toString(); + } + + @Override public void classBegin(int access, String name) { + className = name; + } + + @Override public void implementsInterfaces(String[] interfaces) { + this.interfaces = interfaces; + } + + @Override public void method(int access, String name, String descriptor) { + this.method = name; + this.methodDescriptor = descriptor; + this.methodAccess = access; + descriptors.add(method); + } + + void set(String name, Collection l) { + if (l.size() == 0) + return; + + set(name, Processor.join(l), "<>"); + } + + public void finish() { + set(COMPONENT_MULTIPLE, multiple); + set(COMPONENT_DYNAMIC, dynamic); + set(COMPONENT_OPTIONAL, optional); + set(COMPONENT_IMPLEMENTATION, clazz.getFQN(), "<>"); + set(COMPONENT_PROPERTIES, properties); + if (version != null) { + set(COMPONENT_VERSION, version, "<>"); + reporter.trace("Component %s is v1.1", map); + } + set(COMPONENT_DESCRIPTORS, descriptors); + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,665 @@ +package aQute.bnd.make.component; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import aQute.bnd.annotation.component.*; +import aQute.bnd.make.metatype.*; +import aQute.bnd.service.*; +import aQute.lib.osgi.*; +import aQute.lib.osgi.Clazz.*; +import aQute.libg.version.*; + +/** + * This class is an analyzer plugin. It looks at the properties and tries to + * find out if the Service-Component header contains the bnd shortut syntax. If + * not, the header is copied to the output, if it does, an XML file is created + * and added to the JAR and the header is modified appropriately. + */ +public class ServiceComponent implements AnalyzerPlugin { + public final static String NAMESPACE_STEM = "http://www.osgi.org/xmlns/scr"; + public final static String JIDENTIFIER = "<>"; + public final static String COMPONENT_NAME = "name:"; + public final static String COMPONENT_FACTORY = "factory:"; + public final static String COMPONENT_SERVICEFACTORY = "servicefactory:"; + public final static String COMPONENT_IMMEDIATE = "immediate:"; + public final static String COMPONENT_ENABLED = "enabled:"; + public final static String COMPONENT_DYNAMIC = "dynamic:"; + public final static String COMPONENT_MULTIPLE = "multiple:"; + public final static String COMPONENT_PROVIDE = "provide:"; + public final static String COMPONENT_OPTIONAL = "optional:"; + public final static String COMPONENT_PROPERTIES = "properties:"; + public final static String COMPONENT_IMPLEMENTATION = "implementation:"; + public final static String COMPONENT_DESIGNATE = "designate:"; + public final static String COMPONENT_DESIGNATEFACTORY = "designateFactory:"; + public final static String COMPONENT_DESCRIPTORS = ".descriptors:"; + + // v1.1.0 + public final static String COMPONENT_VERSION = "version:"; + public final static String COMPONENT_CONFIGURATION_POLICY = "configuration-policy:"; + public final static String COMPONENT_MODIFIED = "modified:"; + public final static String COMPONENT_ACTIVATE = "activate:"; + public final static String COMPONENT_DEACTIVATE = "deactivate:"; + + final static Map EMPTY = Collections.emptyMap(); + + public final static String[] componentDirectives = new String[] { + COMPONENT_FACTORY, COMPONENT_IMMEDIATE, COMPONENT_ENABLED, COMPONENT_DYNAMIC, + COMPONENT_MULTIPLE, COMPONENT_PROVIDE, COMPONENT_OPTIONAL, COMPONENT_PROPERTIES, + COMPONENT_IMPLEMENTATION, COMPONENT_SERVICEFACTORY, COMPONENT_VERSION, + COMPONENT_CONFIGURATION_POLICY, COMPONENT_MODIFIED, COMPONENT_ACTIVATE, + COMPONENT_DEACTIVATE, COMPONENT_NAME, COMPONENT_DESCRIPTORS, COMPONENT_DESIGNATE, + COMPONENT_DESIGNATEFACTORY }; + + public final static Set SET_COMPONENT_DIRECTIVES = new HashSet( + Arrays.asList(componentDirectives)); + + public final static Set SET_COMPONENT_DIRECTIVES_1_1 = // + new HashSet( + Arrays.asList( + COMPONENT_VERSION, + COMPONENT_CONFIGURATION_POLICY, + COMPONENT_MODIFIED, + COMPONENT_ACTIVATE, + COMPONENT_DEACTIVATE)); + + public boolean analyzeJar(Analyzer analyzer) throws Exception { + + ComponentMaker m = new ComponentMaker(analyzer); + + Map> l = m.doServiceComponent(); + + analyzer.setProperty(Constants.SERVICE_COMPONENT, Processor.printClauses(l)); + + analyzer.getInfo(m, "Service-Component: "); + m.close(); + + return false; + } + + private static class ComponentMaker extends Processor { + Analyzer analyzer; + + ComponentMaker(Analyzer analyzer) { + super(analyzer); + this.analyzer = analyzer; + } + + /** + * Iterate over the Service Component entries. There are two cases: + *
    + *
  1. An XML file reference
  2. + *
  3. A FQN/wildcard with a set of attributes
  4. + *
+ * + * An XML reference is immediately expanded, an FQN/wildcard is more + * complicated and is delegated to + * {@link #componentEntry(Map, String, Map)}. + * + * @throws Exception + */ + Map> doServiceComponent() throws Exception { + Map> serviceComponents = newMap(); + String header = getProperty(SERVICE_COMPONENT); + Map> sc = parseHeader(header); + + for (Map.Entry> entry : sc.entrySet()) { + String name = entry.getKey(); + Map info = entry.getValue(); + + try { + if (name.indexOf('/') >= 0 || name.endsWith(".xml")) { + // Normal service component, we do not process it + serviceComponents.put(name, EMPTY); + } else { + componentEntry(serviceComponents, name, info); + } + } catch (Exception e) { + e.printStackTrace(); + error("Invalid Service-Component header: %s %s, throws %s", name, info, e); + } + } + return serviceComponents; + } + + /** + * Parse an entry in the Service-Component header. This header supports + * the following types: + *
    + *
  1. An FQN + attributes describing a component
  2. + *
  3. A wildcard expression for finding annotated components.
  4. + *
+ * The problem is the distinction between an FQN and a wildcard because + * an FQN can also be used as a wildcard. + * + * If the info specifies {@link Constants#NOANNOTATIONS} then wildcards + * are an error and the component must be fully described by the info. + * Otherwise the FQN/wildcard is expanded into a list of classes with + * annotations. If this list is empty, the FQN case is interpreted as a + * complete component definition. For the wildcard case, it is checked + * if any matching classes for the wildcard have been compiled for a + * class file format that does not support annotations, this can be a + * problem with JSR14 who silently ignores annotations. An error is + * reported in such a case. + * + * @param serviceComponents + * @param name + * @param info + * @throws Exception + * @throws IOException + */ + private void componentEntry(Map> serviceComponents, + String name, Map info) throws Exception, IOException { + + boolean annotations = !Processor.isTrue(info.get(NOANNOTATIONS)); + boolean fqn = Verifier.isFQN(name); + + if (annotations) { + + // Annotations possible! + + Collection annotatedComponents = analyzer.getClasses("", + QUERY.ANNOTATION.toString(), Component.class.getName(), // + QUERY.NAMED.toString(), name // + ); + + if (fqn) { + if (annotatedComponents.isEmpty()) { + + // No annotations, fully specified in header + + createComponentResource(serviceComponents, name, info); + } else { + + // We had a FQN so expect only one + + for (Clazz c : annotatedComponents) { + annotated(serviceComponents, c, info); + } + } + } else { + + // We did not have an FQN, so expect the use of wildcards + + if (annotatedComponents.isEmpty()) + checkAnnotationsFeasible(name); + else + for (Clazz c : annotatedComponents) { + annotated(serviceComponents, c, info); + } + } + } else { + // No annotations + if (fqn) + createComponentResource(serviceComponents, name, info); + else + error("Set to %s but entry %s is not an FQN ", NOANNOTATIONS, name); + + } + } + + /** + * Check if annotations are actually feasible looking at the class + * format. If the class format does not provide annotations then it is + * no use specifying annotated components. + * + * @param name + * @return + * @throws Exception + */ + private Collection checkAnnotationsFeasible(String name) throws Exception { + Collection not = analyzer.getClasses("", QUERY.NAMED.toString(), name // + ); + + if (not.isEmpty()) + if ( "*".equals(name)) + return not; + else + error("Specified %s but could not find any class matching this pattern", name); + + for (Clazz c : not) { + if (c.getFormat().hasAnnotations()) + return not; + } + + warning("Wildcards are used (%s) requiring annotations to decide what is a component. Wildcard maps to classes that are compiled with java.target < 1.5. Annotations were introduced in Java 1.5", + name); + + return not; + } + + void annotated(Map> components, Clazz c, + Map info) throws Exception { + // Get the component definition + // from the annotations + Map map = ComponentAnnotationReader.getDefinition(c, this); + + // Pick the name, the annotation can override + // the name. + String localname = map.get(COMPONENT_NAME); + if (localname == null) + localname = c.getFQN(); + + // Override the component info without manifest + // entries. We merge the properties though. + + String merged = Processor.merge(info.remove(COMPONENT_PROPERTIES), + map.remove(COMPONENT_PROPERTIES)); + if (merged != null && merged.length() > 0) + map.put(COMPONENT_PROPERTIES, merged); + map.putAll(info); + createComponentResource(components, localname, map); + } + + private void createComponentResource(Map> components, + String name, Map info) throws IOException { + + // We can override the name in the parameters + if (info.containsKey(COMPONENT_NAME)) + name = info.get(COMPONENT_NAME); + + // Assume the impl==name, but allow override + String impl = name; + if (info.containsKey(COMPONENT_IMPLEMENTATION)) + impl = info.get(COMPONENT_IMPLEMENTATION); + + // Check if such a class exists + analyzer.referTo(impl); + + boolean designate = designate(name, info.get(COMPONENT_DESIGNATE), false) + || designate(name, info.get(COMPONENT_DESIGNATEFACTORY), true); + + // If we had a designate, we want a default configuration policy of + // require. + if (designate && info.get(COMPONENT_CONFIGURATION_POLICY) == null) + info.put(COMPONENT_CONFIGURATION_POLICY, "require"); + + // We have a definition, so make an XML resources + Resource resource = createComponentResource(name, impl, info); + analyzer.getJar().putResource("OSGI-INF/" + name + ".xml", resource); + + components.put("OSGI-INF/" + name + ".xml", EMPTY); + + } + + /** + * Create a Metatype and Designate record out of the given + * configurations. + * + * @param name + * @param config + */ + private boolean designate(String name, String config, boolean factory) { + if (config == null) + return false; + + for (String c : Processor.split(config)) { + Clazz clazz = analyzer.getClassspace().get(Clazz.fqnToPath(c)); + if (clazz != null) { + analyzer.referTo(c); + MetaTypeReader r = new MetaTypeReader(clazz, analyzer); + r.setDesignate(name, factory); + String rname = "OSGI-INF/metatype/" + name + ".xml"; + + analyzer.getJar().putResource(rname, r); + } else { + analyzer.error( + "Cannot find designated configuration class %s for component %s", c, + name); + } + } + return true; + } + + /** + * Create the resource for a DS component. + * + * @param list + * @param name + * @param info + * @throws UnsupportedEncodingException + */ + Resource createComponentResource(String name, String impl, Map info) + throws IOException { + String namespace = getNamespace(info); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, Constants.DEFAULT_CHARSET)); + pw.println(""); + if (namespace != null) + pw.print(""); + + // Allow override of the implementation when people + // want to choose their own name + pw.println(" "); + + String provides = info.get(COMPONENT_PROVIDE); + boolean servicefactory = Processor.isTrue(info.get(COMPONENT_SERVICEFACTORY)); + + if (servicefactory && Processor.isTrue(info.get(COMPONENT_IMMEDIATE))) { + // TODO can become error() if it is up to me + warning("For a Service Component, the immediate option and the servicefactory option are mutually exclusive for %(%s)", + name, impl); + } + provide(pw, provides, servicefactory, impl); + properties(pw, info); + reference(info, pw); + + if (namespace != null) + pw.println(""); + else + pw.println(""); + + pw.close(); + byte[] data = out.toByteArray(); + out.close(); + return new EmbeddedResource(data, 0); + } + + private void doAttribute(PrintWriter pw, String value, String name, String... matches) { + if (value != null) { + if (matches.length != 0) { + if (matches.length == 1 && matches[0].equals(JIDENTIFIER)) { + if (!Verifier.isIdentifier(value)) + error("Component attribute %s has value %s but is not a Java identifier", + name, value); + } else { + + if (!Verifier.isMember(value, matches)) + error("Component attribute %s has value %s but is not a member of %s", + name, value, Arrays.toString(matches)); + } + } + pw.print(" "); + pw.print(name); + pw.print("='"); + pw.print(value); + pw.print("'"); + } + } + + /** + * Check if we need to use the v1.1 namespace (or later). + * + * @param info + * @return + */ + private String getNamespace(Map info) { + String version = info.get(COMPONENT_VERSION); + if (version != null) { + try { + Version v = new Version(version); + return NAMESPACE_STEM + "/v" + v; + } catch (Exception e) { + error("version: specified on component header but not a valid version: " + + version); + return null; + } + } + for (String key : info.keySet()) { + if (SET_COMPONENT_DIRECTIVES_1_1.contains(key)) { + return NAMESPACE_STEM + "/v1.1.0"; + } + } + return null; + } + + /** + * Print the Service-Component properties element + * + * @param pw + * @param info + */ + void properties(PrintWriter pw, Map info) { + Collection properties = split(info.get(COMPONENT_PROPERTIES)); + for (Iterator p = properties.iterator(); p.hasNext();) { + String clause = p.next(); + int n = clause.indexOf('='); + if (n <= 0) { + error("Not a valid property in service component: " + clause); + } else { + String type = null; + String name = clause.substring(0, n); + if (name.indexOf('@') >= 0) { + String parts[] = name.split("@"); + name = parts[1]; + type = parts[0]; + } else if (name.indexOf(':') >= 0) { + String parts[] = name.split(":"); + name = parts[0]; + type = parts[1]; + } + String value = clause.substring(n + 1).trim(); + // TODO verify validity of name and value. + pw.print(" 1) { + pw.println(">"); + for (String part : parts) { + pw.println(part); + } + pw.println(""); + } else { + pw.print(" value='"); + pw.print(parts[0]); + pw.println("'/>"); + } + } + } + } + + /** + * @param pw + * @param provides + */ + void provide(PrintWriter pw, String provides, boolean servicefactory, String impl) { + if (provides != null) { + if (!servicefactory) + pw.println(" "); + else + pw.println(" "); + + StringTokenizer st = new StringTokenizer(provides, ","); + while (st.hasMoreTokens()) { + String interfaceName = st.nextToken(); + pw.println(" "); + analyzer.referTo(interfaceName); + + // TODO verifies the impl. class extends or implements the + // interface + } + pw.println(" "); + } else if (servicefactory) + warning("The servicefactory:=true directive is set but no service is provided, ignoring it"); + } + + public final static Pattern REFERENCE = Pattern.compile("([^(]+)(\\(.+\\))?"); + + /** + * @param info + * @param pw + */ + + void reference(Map info, PrintWriter pw) { + Collection dynamic = new ArrayList(split(info.get(COMPONENT_DYNAMIC))); + Collection optional = new ArrayList(split(info.get(COMPONENT_OPTIONAL))); + Collection multiple = new ArrayList(split(info.get(COMPONENT_MULTIPLE))); + + Collection descriptors = split(info.get(COMPONENT_DESCRIPTORS)); + + for (Map.Entry entry : info.entrySet()) { + + // Skip directives + String referenceName = entry.getKey(); + if (referenceName.endsWith(":")) { + if (!SET_COMPONENT_DIRECTIVES.contains(referenceName)) + error("Unrecognized directive in Service-Component header: " + + referenceName); + continue; + } + + // Parse the bind/unbind methods from the name + // if set. They are separated by '/' + String bind = null; + String unbind = null; + + boolean unbindCalculated = false; + + if (referenceName.indexOf('/') >= 0) { + String parts[] = referenceName.split("/"); + referenceName = parts[0]; + bind = parts[1]; + if (parts.length > 2) { + unbind = parts[2]; + } else { + unbindCalculated = true; + if (bind.startsWith("add")) + unbind = bind.replaceAll("add(.+)", "remove$1"); + else + unbind = "un" + bind; + } + } else if (Character.isLowerCase(referenceName.charAt(0))) { + unbindCalculated = true; + bind = "set" + Character.toUpperCase(referenceName.charAt(0)) + + referenceName.substring(1); + unbind = "un" + bind; + } + + String interfaceName = entry.getValue(); + if (interfaceName == null || interfaceName.length() == 0) { + error("Invalid Interface Name for references in Service Component: " + + referenceName + "=" + interfaceName); + continue; + } + + // If we have descriptors, we have analyzed the component. + // So why not check the methods + if (descriptors.size() > 0) { + // Verify that the bind method exists + if (!descriptors.contains(bind)) + error("The bind method %s for %s not defined", bind, referenceName); + + // Check if the unbind method exists + if (!descriptors.contains(unbind)) { + if (unbindCalculated) + // remove it + unbind = null; + else + error("The unbind method %s for %s not defined", unbind, referenceName); + } + } + // Check tje cardinality by looking at the last + // character of the value + char c = interfaceName.charAt(interfaceName.length() - 1); + if ("?+*~".indexOf(c) >= 0) { + if (c == '?' || c == '*' || c == '~') + optional.add(referenceName); + if (c == '+' || c == '*') + multiple.add(referenceName); + if (c == '+' || c == '*' || c == '?') + dynamic.add(referenceName); + interfaceName = interfaceName.substring(0, interfaceName.length() - 1); + } + + // Parse the target from the interface name + // The target is a filter. + String target = null; + Matcher m = REFERENCE.matcher(interfaceName); + if (m.matches()) { + interfaceName = m.group(1); + target = m.group(2); + } + + analyzer.referTo(interfaceName); + + pw.printf(" "); + } + } + } + + /** + * Escape a string, do entity conversion. + */ + static String escape(String s) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '&': + sb.append("&"); + break; + case '\'': + sb.append("""); + break; + default: + sb.append(c); + break; + } + } + return sb.toString(); + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/component/ServiceComponent.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java?rev=1185095&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java Mon Oct 17 10:31:43 2011 @@ -0,0 +1,95 @@ +package aQute.bnd.make.coverage; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import aQute.lib.osgi.*; +import aQute.lib.osgi.Clazz.*; + +/** + * This class can create a coverage table between two classspaces. The + * destination class space is used to create a table of methods. All source + * methods that refer to a specific dest are then filled into the table. + * + */ +public class Coverage { + + /** + * Create a cross reference table from source to dest. + * + * @param source + * The methods that refer to dest + * @param dest + * The methods that are being referred to + * @return A mapping of source methods to destination methods. + * @throws IOException + */ + public static Map> getCrossRef( + Collection source, Collection dest) + throws Exception { + final Map> catalog = buildCatalog(dest); + crossRef(source, catalog); + return catalog; + } + + private static void crossRef(Collection source, + final Map> catalog) throws Exception { + for (final Clazz clazz : source) { + clazz.parseClassFileWithCollector(new ClassDataCollector() { + MethodDef source; + + public void implementsInterfaces(String names[]) { + MethodDef def = new MethodDef(0, clazz.getFQN(), + "", "()V"); + for (String interfaceName : names) { + interfaceName = Clazz.internalToFqn(interfaceName); + for (Map.Entry> entry : catalog + .entrySet()) { + String catalogClass = entry.getKey().clazz; + List references = entry.getValue(); + + if (catalogClass.equals(interfaceName)) { + references.add(def); + } + } + } + } + + // Method definitions + public void method(MethodDef source) { + this.source = source; + } + + public void reference(MethodDef reference) { + List references = catalog.get(reference); + if (references != null) { + references.add(source); + } + } + }); + } + } + + private static Map> buildCatalog( + Collection sources) throws Exception { + final Map> catalog = new TreeMap>(); + for (final Clazz clazz : sources) { + clazz.parseClassFileWithCollector(new ClassDataCollector() { + + public boolean classStart(int access, String name) { + return clazz.isPublic(); + } + + public void method(MethodDef source) { + if (java.lang.reflect.Modifier.isPublic(source.access) + || Modifier.isProtected(source.access)) + catalog.put(source, new ArrayList()); + } + + }); + } + return catalog; + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/make/coverage/Coverage.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision