You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by mc...@apache.org on 2009/07/13 12:06:50 UTC
svn commit: r793527 [4/7] - in /felix/trunk/bundleplugin: ./
src/main/java/aQute/ src/main/java/aQute/bnd/
src/main/java/aQute/bnd/build/ src/main/java/aQute/bnd/help/
src/main/java/aQute/bnd/make/ src/main/java/aQute/bnd/service/
src/main/java/aQute/b...
Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,1135 @@
+/* Copyright 2006 aQute SARL
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import aQute.bnd.make.*;
+import aQute.bnd.service.*;
+
+/**
+ * Include-Resource: ( [name '=' ] file )+
+ *
+ * Private-Package: package-decl ( ',' package-decl )*
+ *
+ * Export-Package: package-decl ( ',' package-decl )*
+ *
+ * Import-Package: package-decl ( ',' package-decl )*
+ *
+ * @version $Revision: 1.17 $
+ */
+public class Builder extends Analyzer {
+ private static final int SPLIT_MERGE_LAST = 1;
+ private static final int SPLIT_MERGE_FIRST = 2;
+ private static final int SPLIT_ERROR = 3;
+ private static final int SPLIT_FIRST = 4;
+ private static final int SPLIT_DEFAULT = 0;
+
+ private static final File[] EMPTY_FILE = new File[0];
+
+ List<File> sourcePath = new ArrayList<File>();
+ Pattern NAME_URL = Pattern
+ .compile("(.*)(http://.*)");
+
+ Make make = new Make(this);
+
+ public Builder(Processor parent) {
+ super(parent);
+ }
+
+ public Builder() {
+ }
+
+ public Jar build() throws Exception {
+ if (getProperty(NOPE) != null)
+ return null;
+
+ String sub = getProperty(SUB);
+ if (sub != null && sub.trim().length() > 0)
+ error("Specified "
+ + SUB
+ + " but calls build() instead of builds() (might be a programmer error)");
+
+ if (getProperty(CONDUIT) != null)
+ error("Specified "
+ + CONDUIT
+ + " but calls build() instead of builds() (might be a programmer error");
+
+ dot = new Jar("dot");
+ addClose(dot);
+ try {
+ long modified = Long.parseLong(getProperty("base.modified"));
+ dot.updateModified(modified, "Base modified");
+ } catch (Exception e) {
+ }
+
+ doExpand(dot);
+ doIncludeResources(dot);
+
+ doConditional(dot);
+
+ // NEW!
+ // Check if we override the calculation of the
+ // manifest. We still need to calculated it because
+ // we need to have analyzed the classpath.
+
+ Manifest manifest = calcManifest();
+
+ String mf = getProperty(MANIFEST);
+ if (mf != null) {
+ File mff = getFile(mf);
+ if (mff.isFile()) {
+ try {
+ InputStream in = new FileInputStream(mff);
+ manifest = new Manifest(in);
+ in.close();
+ } catch (Exception e) {
+ error(MANIFEST + " while reading manifest file", e);
+ }
+ } else {
+ error(MANIFEST + ", no such file " + mf);
+ }
+ }
+
+ if (getProperty(NOMANIFEST) == null)
+ dot.setManifest(manifest);
+ else
+ dot.setNoManifest(true);
+
+ // This must happen after we analyzed so
+ // we know what it is on the classpath
+ addSources(dot);
+ if (getProperty(POM) != null)
+ doPom(dot);
+
+ doVerify(dot);
+
+ if (dot.getResources().isEmpty())
+ error("The JAR is empty");
+
+ dot.updateModified(lastModified(), "Last Modified Processor");
+ dot.setName(getBsn());
+
+ sign(dot);
+ return dot;
+ }
+
+ /**
+ * Sign the jar file.
+ *
+ * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
+ * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
+ *
+ * @return
+ */
+
+ void sign(Jar jar) throws Exception {
+ String signing = getProperty("-sign");
+ if (signing == null)
+ return;
+
+ trace("Signing %s, with %s", getBsn(), signing);
+ List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+
+ Map<String, Map<String, String>> infos = parseHeader(signing);
+ for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
+ for (SignerPlugin signer : signers) {
+ signer.sign(this, entry.getKey());
+ }
+ }
+ }
+
+ public boolean hasSources() {
+ return isTrue(getProperty(SOURCES));
+ }
+
+ protected String getImportPackages() {
+ String ip = super.getImportPackages();
+ if (ip != null)
+ return ip;
+
+ return "*";
+ }
+
+ private void doConditional(Jar dot) throws IOException {
+ Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
+ int size;
+ do {
+ size = dot.getDirectories().size();
+ analyze();
+ analyzed = false;
+ Map<String, Map<String, String>> imports = getImports();
+
+ // Match the packages specified in conditionals
+ // against the imports. Any match must become a
+ // Private-Package
+ Map<String, Map<String, String>> filtered = merge(
+ CONDITIONAL_PACKAGE, conditionals, imports,
+ new HashSet<String>(), null);
+
+ // Imports can also specify a private import. These
+ // packages must also be copied to the bundle
+ for (Map.Entry<String, Map<String, String>> entry : getImports()
+ .entrySet()) {
+ String type = entry.getValue().get(IMPORT_DIRECTIVE);
+ if (type != null && type.equals("private"))
+ filtered.put(entry.getKey(), entry.getValue());
+ }
+
+ // remove existing packages to prevent merge errors
+ filtered.keySet().removeAll(dot.getPackages());
+ doExpand(dot, CONDITIONAL_PACKAGE + " Private imports",
+ replaceWitInstruction(filtered, CONDITIONAL_PACKAGE), false);
+ } while (dot.getDirectories().size() > size);
+ analyzed = true;
+ }
+
+ /**
+ * Intercept the call to analyze and cleanup versions after we have analyzed
+ * the setup. We do not want to cleanup if we are going to verify.
+ */
+
+ public void analyze() throws IOException {
+ super.analyze();
+ cleanupVersion(imports);
+ cleanupVersion(exports);
+ String version = getProperty(BUNDLE_VERSION);
+ if (version != null)
+ setProperty(BUNDLE_VERSION, cleanupVersion(version));
+ }
+
+ public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap
+ .entrySet().iterator(); e.hasNext();) {
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ Map<String, String> attributes = entry.getValue();
+ if (attributes.containsKey("version")) {
+ attributes.put("version", cleanupVersion(attributes
+ .get("version")));
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ private void addSources(Jar dot) {
+ if (!hasSources())
+ return;
+
+ Set<String> packages = new HashSet<String>();
+
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ getProperties().store(out, "Generated by BND, at " + new Date());
+ dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
+ .toByteArray(), 0));
+ out.close();
+ } catch (Exception e) {
+ error("Can not embed bnd file in JAR: " + e);
+ }
+
+ for (Iterator<String> cpe = classspace.keySet().iterator(); cpe
+ .hasNext();) {
+ String path = cpe.next();
+ path = path.substring(0, path.length() - ".class".length())
+ + ".java";
+ String pack = getPackage(path).replace('.', '/');
+ if (pack.length() > 1)
+ pack = pack + "/";
+ boolean found = false;
+ String[] fixed = { "packageinfo", "package.html",
+ "module-info.java", "package-info.java" };
+ for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
+ File root = i.next();
+ File f = getFile(root, path);
+ if (f.exists()) {
+ found = true;
+ if (!packages.contains(pack)) {
+ packages.add(pack);
+ File bdir = getFile(root, pack);
+ for (int j = 0; j < fixed.length; j++) {
+ File ff = getFile(bdir, fixed[j]);
+ if (ff.isFile()) {
+ dot.putResource("OSGI-OPT/src/" + pack
+ + fixed[j], new FileResource(ff));
+ }
+ }
+ }
+ dot
+ .putResource("OSGI-OPT/src/" + path,
+ new FileResource(f));
+ }
+ }
+ if (!found) {
+ for (Jar jar : classpath) {
+ Resource resource = jar.getResource(path);
+ if (resource != null) {
+ dot.putResource("OSGI-OPT/src", resource);
+ } else {
+ resource = jar.getResource("OSGI-OPT/src/" + path);
+ if (resource != null) {
+ dot.putResource("OSGI-OPT/src", resource);
+ }
+ }
+ }
+ }
+ if (getSourcePath().isEmpty())
+ warning("Including sources but " + SOURCEPATH
+ + " does not contain any source directories ");
+ // TODO copy from the jars where they came from
+ }
+ }
+
+ boolean firstUse = true;
+
+ public Collection<File> getSourcePath() {
+ if (firstUse) {
+ firstUse = false;
+ String sp = getProperty(SOURCEPATH);
+ if (sp != null) {
+ Map<String, Map<String, String>> map = parseHeader(sp);
+ for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
+ String file = i.next();
+ if (!isDuplicate(file)) {
+ File f = getFile(file);
+ if (!f.isDirectory()) {
+ error("Adding a sourcepath that is not a directory: "
+ + f);
+ } else {
+ sourcePath.add(f);
+ }
+ }
+ }
+ }
+ }
+ return sourcePath;
+ }
+
+ private void doVerify(Jar dot) throws Exception {
+ Verifier verifier = new Verifier(dot, getProperties());
+ verifier.setPedantic(isPedantic());
+
+ // Give the verifier the benefit of our analysis
+ // prevents parsing the files twice
+ verifier.setClassSpace(classspace, contained, referred, uses);
+ verifier.verify();
+ getInfo(verifier);
+ }
+
+ private void doExpand(Jar jar) throws IOException {
+ if (getClasspath().size() == 0
+ && (getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
+ warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
+
+ Map<Instruction, Map<String, String>> privateMap = replaceWitInstruction(
+ getHeader(PRIVATE_PACKAGE), PRIVATE_PACKAGE);
+ Map<Instruction, Map<String, String>> exportMap = replaceWitInstruction(
+ getHeader(EXPORT_PACKAGE), EXPORT_PACKAGE);
+
+ if (isTrue(getProperty(Constants.UNDERTEST))) {
+ privateMap.putAll(replaceWitInstruction(parseHeader(getProperty(
+ Constants.TESTPACKAGES, "test;presence:=optional")),
+ TESTPACKAGES));
+ }
+ if (!privateMap.isEmpty())
+ doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
+
+ if (!exportMap.isEmpty()) {
+ Jar exports = new Jar("exports");
+ doExpand(exports, "Export-Package", exportMap, true);
+ jar.addAll(exports);
+ exports.close();
+ }
+
+ if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()) {
+ warning("Neither Export-Package, Private-Package, -testpackages is set, therefore no packages will be included");
+ }
+ }
+
+ /**
+ *
+ * @param jar
+ * @param name
+ * @param instructions
+ */
+ private void doExpand(Jar jar, String name,
+ Map<Instruction, Map<String, String>> instructions,
+ boolean mandatory) {
+ Set<Instruction> superfluous = removeMarkedDuplicates(instructions
+ .keySet());
+
+ for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
+ Jar now = c.next();
+ doExpand(jar, instructions, now, superfluous);
+ }
+
+ if (mandatory && superfluous.size() > 0) {
+ StringBuffer sb = new StringBuffer();
+ String del = "Instructions in " + name + " that are never used: ";
+ for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
+ Instruction p = i.next();
+ sb.append(del);
+ sb.append(p.getPattern());
+ del = ", ";
+ }
+ warning(sb.toString());
+ }
+ }
+
+ /**
+ * Iterate over each directory in the class path entry and check if that
+ * directory is a desired package.
+ *
+ * @param included
+ * @param classpathEntry
+ */
+ private void doExpand(Jar jar,
+ Map<Instruction, Map<String, String>> included, Jar classpathEntry,
+ Set<Instruction> superfluous) {
+
+ loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
+ .getDirectories().entrySet()) {
+ String path = directory.getKey();
+
+ if (doNotCopy.matcher(getName(path)).matches())
+ continue;
+
+ if (directory.getValue() == null)
+ continue;
+
+ String pack = path.replace('/', '.');
+ Instruction instr = matches(included, pack, superfluous);
+ if (instr != null) {
+ // System.out.println("Pattern match: " + pack + " " +
+ // instr.getPattern() + " " + instr.isNegated());
+ if (!instr.isNegated()) {
+ Map<String, Resource> contents = directory.getValue();
+
+ // What to do with split packages? Well if this
+ // directory already exists, we will check the strategy
+ // and react accordingly.
+ boolean overwriteResource = true;
+ if (jar.hasDirectory(path)) {
+ Map<String, String> directives = included.get(instr);
+
+ switch (getSplitStrategy((String) directives
+ .get(SPLIT_PACKAGE_DIRECTIVE))) {
+ case SPLIT_MERGE_LAST:
+ overwriteResource = true;
+ break;
+
+ case SPLIT_MERGE_FIRST:
+ overwriteResource = false;
+ break;
+
+ case SPLIT_ERROR:
+ error(diagnostic(pack, getClasspath(),
+ classpathEntry.source));
+ continue loop;
+
+ case SPLIT_FIRST:
+ continue loop;
+
+ default:
+ warning(diagnostic(pack, getClasspath(),
+ classpathEntry.source));
+ overwriteResource = false;
+ break;
+ }
+ }
+
+ jar.addDirectory(contents, overwriteResource);
+
+ String key = path + "/bnd.info";
+ Resource r = jar.getResource(key);
+ if (r != null)
+ jar.putResource(key, new PreprocessResource(this, r));
+
+ if (hasSources()) {
+ String srcPath = "OSGI-OPT/src/" + path;
+ Map<String, Resource> srcContents = classpathEntry
+ .getDirectories().get(srcPath);
+ if (srcContents != null) {
+ jar.addDirectory(srcContents, overwriteResource);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Analyze the classpath for a split package
+ *
+ * @param pack
+ * @param classpath
+ * @param source
+ * @return
+ */
+ private String diagnostic(String pack, List<Jar> classpath, File source) {
+ // Default is like merge-first, but with a warning
+ // Find the culprits
+ pack = pack.replace('.', '/');
+ List<Jar> culprits = new ArrayList<Jar>();
+ for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
+ Jar culprit = (Jar) i.next();
+ if (culprit.getDirectories().containsKey(pack)) {
+ culprits.add(culprit);
+ }
+ }
+ return "Split package "
+ + pack
+ + "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
+ + "Package found in " + culprits + "\n"
+ + "Reference from " + source + "\n" + "Classpath "
+ + classpath;
+ }
+
+ private int getSplitStrategy(String type) {
+ if (type == null)
+ return SPLIT_DEFAULT;
+
+ if (type.equals("merge-last"))
+ return SPLIT_MERGE_LAST;
+
+ if (type.equals("merge-first"))
+ return SPLIT_MERGE_FIRST;
+
+ if (type.equals("error"))
+ return SPLIT_ERROR;
+
+ if (type.equals("first"))
+ return SPLIT_FIRST;
+
+ error("Invalid strategy for split-package: " + type);
+ return SPLIT_DEFAULT;
+ }
+
+ private Map<Instruction, Map<String, String>> replaceWitInstruction(
+ Map<String, Map<String, String>> header, String type) {
+ Map<Instruction, Map<String, String>> map = newMap();
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = header
+ .entrySet().iterator(); e.hasNext();) {
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ String pattern = entry.getKey();
+ Instruction instr = Instruction.getPattern(pattern);
+ String presence = entry.getValue().get(PRESENCE_DIRECTIVE);
+ if ("optional".equals(presence))
+ instr.setOptional();
+ map.put(instr, entry.getValue());
+ }
+ return map;
+ }
+
+ private Instruction matches(
+ Map<Instruction, Map<String, String>> instructions, String pack,
+ Set<Instruction> superfluousPatterns) {
+ for (Instruction pattern : instructions.keySet()) {
+ if (pattern.matches(pack)) {
+ superfluousPatterns.remove(pattern);
+ return pattern;
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Map<String, String>> getHeader(String string) {
+ if (string == null)
+ return Collections.emptyMap();
+ return parseHeader(getProperty(string));
+ }
+
+ /**
+ * Parse the Bundle-Includes header. Files in the bundles Include header are
+ * included in the jar. The source can be a directory or a file.
+ *
+ * @throws IOException
+ * @throws FileNotFoundException
+ */
+ private void doIncludeResources(Jar jar) throws Exception {
+ String includes = getProperty("Bundle-Includes");
+ if (includes == null) {
+ includes = getProperty(INCLUDERESOURCE);
+ if (includes == null)
+ includes = getProperty("Include-Resource");
+ } else
+ warning("Please use -includeresource instead of Bundle-Includes");
+
+ if (includes == null)
+ return;
+
+ Map<String, Map<String, String>> clauses = parseHeader(includes);
+
+ for (Iterator<Map.Entry<String, Map<String, String>>> i = clauses
+ .entrySet().iterator(); i.hasNext();) {
+ Map.Entry<String, Map<String, String>> entry = i.next();
+ doIncludeResource(jar, entry.getKey(), entry.getValue());
+ }
+ }
+
+ private void doIncludeResource(Jar jar, String name,
+ Map<String, String> extra) throws ZipException, IOException,
+ Exception {
+ boolean preprocess = false;
+ if (name.startsWith("{") && name.endsWith("}")) {
+ preprocess = true;
+ name = name.substring(1, name.length() - 1).trim();
+ }
+
+ if (name.startsWith("@")) {
+ extractFromJar(jar, name.substring(1));
+ } else
+ /*
+ * NEW
+ */
+ if (extra.containsKey("literal")) {
+ String literal = (String) extra.get("literal");
+ Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
+ String x = (String) extra.get("extra");
+ if (x != null)
+ r.setExtra(x);
+ jar.putResource(name, r);
+ } else {
+ String source;
+ File sourceFile;
+ String destinationPath;
+
+ String parts[] = name.split("\\s*=\\s*");
+ if (parts.length == 1) {
+ // Just a copy, destination path defined by
+ // source path.
+ source = parts[0];
+ sourceFile = getFile(source);
+ // Directories should be copied to the root
+ // but files to their file name ...
+ if (sourceFile.isDirectory())
+ destinationPath = "";
+ else
+ destinationPath = sourceFile.getName();
+ } else {
+ source = parts[1];
+ sourceFile = getFile(source);
+ destinationPath = parts[0];
+
+ // Handle directives
+ if (sourceFile.isDirectory()) {
+ String filter = extra.get("filter:");
+ boolean flatten = isTrue(extra.get("flatten:"));
+ boolean recursive = true;
+ String directive = extra.get("recursive:");
+ if (directive != null) {
+ recursive = isTrue(directive);
+ }
+
+ InstructionFilter iFilter = null;
+ if (filter != null) {
+ iFilter = new InstructionFilter(Instruction.getPattern(filter), recursive);
+ } else {
+ iFilter = new InstructionFilter(null, recursive);
+ }
+
+ destinationPath = checkDestinationPath(destinationPath);
+
+ File[] files = resolveFiles(sourceFile, iFilter, recursive);
+ for (File file : files) {
+ String dp;
+ if (flatten) {
+ if (destinationPath.length() == 0) {
+ dp = file.getName();
+ } else {
+ dp = destinationPath + File.separator + file.getName();
+ }
+ } else {
+ dp = destinationPath + file.getParentFile().getAbsolutePath().substring(sourceFile.getAbsolutePath().length());
+ if (dp.length() > 0) {
+ dp += File.separator + file.getName();
+ } else {
+ dp = file.getName();
+ }
+ }
+ copy(jar, dp, file, preprocess, extra);
+ }
+ return;
+ }
+ }
+
+ destinationPath = checkDestinationPath(destinationPath);
+
+ if (!sourceFile.exists()) {
+ noSuchFile(jar, name, extra, source, destinationPath);
+ } else
+ copy(jar, destinationPath, sourceFile, preprocess, extra);
+ }
+ }
+
+ private String checkDestinationPath(String destinationPath) {
+
+ // Some people insist on ending a directory with
+ // a slash ... it now also works if you do /=dir
+ if (destinationPath.endsWith("/"))
+ destinationPath = destinationPath.substring(0, destinationPath
+ .length() - 1);
+ return destinationPath;
+ }
+
+ private File[] resolveFiles(File dir, FileFilter filter, boolean recursive) {
+ return resolveFiles(dir, filter, null, recursive);
+ }
+
+ private File[] resolveFiles(File dir, FileFilter filter, File[] files, boolean recursive) {
+ if (files == null) {
+ files = EMPTY_FILE;
+ }
+
+ if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
+ return files;
+ }
+
+ File[] fs = dir.listFiles(filter);
+ for (File file : fs) {
+ if (file.isDirectory()) {
+ if (recursive) {
+ files = resolveFiles(file, filter, files, recursive);
+ }
+ } else {
+ if (files.length == 0) {
+ files = new File[] { file };
+ } else {
+ File[] newFiles = new File[files.length + 1];
+ System.arraycopy(files, 0, newFiles, 0, files.length);
+ newFiles[newFiles.length - 1] = file;
+ files = newFiles;
+ }
+ }
+ }
+ return files;
+ }
+
+ private void noSuchFile(Jar jar, String clause, Map<String, String> extra,
+ String source, String destinationPath) throws Exception {
+ Jar src = getJarFromName(source, "Include-Resource " + source);
+ if (src != null) {
+ JarResource jarResource = new JarResource(src);
+ jar.putResource(destinationPath, jarResource);
+ } else {
+ Resource lastChance = make.process(source);
+ if (lastChance != null) {
+ String x = extra.get("extra");
+ if ( x != null )
+ lastChance.setExtra(x);
+ jar.putResource(destinationPath, lastChance);
+ } else
+ error("Input file does not exist: " + source);
+ }
+ }
+
+ /**
+ * Extra resources from a Jar and add them to the given jar. The clause is
+ * the
+ *
+ * @param jar
+ * @param clauses
+ * @param i
+ * @throws ZipException
+ * @throws IOException
+ */
+ private void extractFromJar(Jar jar, String name) throws ZipException,
+ IOException {
+ // Inline all resources and classes from another jar
+ // optionally appended with a modified regular expression
+ // like @zip.jar!/META-INF/MANIFEST.MF
+ int n = name.lastIndexOf("!/");
+ Pattern filter = null;
+ if (n > 0) {
+ String fstring = name.substring(n + 2);
+ name = name.substring(0, n);
+ filter = wildcard(fstring);
+ }
+ Jar sub = getJarFromName(name, "extract from jar");
+ if (sub == null)
+ error("Can not find JAR file " + name);
+ else
+ jar.addAll(sub, filter);
+ }
+
+ private Pattern wildcard(String spec) {
+ StringBuffer sb = new StringBuffer();
+ for (int j = 0; j < spec.length(); j++) {
+ char c = spec.charAt(j);
+ switch (c) {
+ case '.':
+ sb.append("\\.");
+ break;
+
+ case '*':
+ // test for ** (all directories)
+ if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
+ sb.append(".*");
+ j++;
+ } else
+ sb.append("[^/]*");
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ String s = sb.toString();
+ try {
+ return Pattern.compile(s);
+ } catch (Exception e) {
+ error("Invalid regular expression on wildcarding: " + spec
+ + " used *");
+ }
+ return null;
+ }
+
+ private void copy(Jar jar, String path, File from, boolean preprocess,
+ Map<String, String> extra) throws Exception {
+ if (doNotCopy.matcher(from.getName()).matches())
+ return;
+
+ if (from.isDirectory()) {
+ String next = path;
+ if (next.length() != 0)
+ next += '/';
+
+ File files[] = from.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ copy(jar, next + files[i].getName(), files[i], preprocess,
+ extra);
+ }
+ } else {
+ if (from.exists()) {
+ Resource resource = new FileResource(from);
+ if (preprocess) {
+ resource = new PreprocessResource(this, resource);
+ }
+ String x = extra.get("extra");
+ if (x != null)
+ resource.setExtra(x);
+ jar.putResource(path, resource);
+ } else {
+ error("Input file does not exist: " + from);
+ }
+ }
+ }
+
+ private String getName(String where) {
+ int n = where.lastIndexOf('/');
+ if (n < 0)
+ return where;
+
+ return where.substring(n + 1);
+ }
+
+ public void setSourcepath(File[] files) {
+ for (int i = 0; i < files.length; i++)
+ addSourcepath(files[i]);
+ }
+
+ public void addSourcepath(File cp) {
+ if (!cp.exists())
+ warning("File on sourcepath that does not exist: " + cp);
+
+ sourcePath.add(cp);
+ }
+
+ /**
+ * Create a POM reseource for Maven containing as much information as
+ * possible from the manifest.
+ *
+ * @param output
+ * @param builder
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ public void doPom(Jar dot) throws FileNotFoundException, IOException {
+ {
+ Manifest manifest = dot.getManifest();
+ String name = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_NAME);
+ String description = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_DESCRIPTION);
+ String docUrl = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_DOCURL);
+ String version = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_VERSION);
+ String bundleVendor = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_VENDOR);
+ ByteArrayOutputStream s = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(s);
+ String bsn = manifest.getMainAttributes().getValue(
+ Analyzer.BUNDLE_SYMBOLICNAME);
+ String licenses = manifest.getMainAttributes().getValue(
+ BUNDLE_LICENSE);
+
+ if (bsn == null) {
+ error("Can not create POM unless Bundle-SymbolicName is set");
+ return;
+ }
+
+ bsn = bsn.trim();
+ int n = bsn.lastIndexOf('.');
+ if (n <= 0) {
+ error("Can not create POM unless Bundle-SymbolicName contains a .");
+ ps.close();
+ s.close();
+ return;
+ }
+ String groupId = bsn.substring(0, n);
+ String artifactId = bsn.substring(n + 1);
+ ps
+ .println("<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>");
+ ps.println(" <modelVersion>4.0.0</modelVersion>");
+ ps.println(" <groupId>" + groupId + "</groupId>");
+
+ n = artifactId.indexOf(';');
+ if (n > 0)
+ artifactId = artifactId.substring(0, n).trim();
+
+ ps.println(" <artifactId>" + artifactId + "</artifactId>");
+ ps.println(" <version>" + version + "</version>");
+ if (description != null) {
+ ps.println(" <description>");
+ ps.print(" ");
+ ps.println(description);
+ ps.println(" </description>");
+ }
+ if (name != null) {
+ ps.print(" <name>");
+ ps.print(name);
+ ps.println("</name>");
+ }
+ if (docUrl != null) {
+ ps.print(" <url>");
+ ps.print(docUrl);
+ ps.println("</url>");
+ }
+
+ if (bundleVendor != null) {
+ Matcher m = NAME_URL.matcher(bundleVendor);
+ String namePart = bundleVendor;
+ String urlPart = null;
+ if (m.matches()) {
+ namePart = m.group(1);
+ urlPart = m.group(2);
+ }
+ ps.println(" <organization>");
+ ps.print(" <name>");
+ ps.print(namePart.trim());
+ ps.println("</name>");
+ if (urlPart != null) {
+ ps.print(" <url>");
+ ps.print(urlPart.trim());
+ ps.println("</url>");
+ }
+ ps.println(" </organization>");
+ }
+ if (licenses != null) {
+ ps.println(" <licenses>");
+ Map<String, Map<String, String>> map = parseHeader(licenses);
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = map
+ .entrySet().iterator(); e.hasNext();) {
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ ps.println(" <license>");
+ Map<String, String> values = entry.getValue();
+ print(ps, values, "name", "name", (String) values
+ .get("url"));
+ print(ps, values, "url", "url", null);
+ print(ps, values, "distribution", "distribution", "repo");
+ ps.println(" </license>");
+ }
+ ps.println(" </licenses>");
+ }
+ ps.println("</project>");
+ ps.close();
+ s.close();
+ dot
+ .putResource("pom.xml", new EmbeddedResource(s
+ .toByteArray(), 0));
+ }
+ }
+
+ /**
+ * Utility function to print a tag from a map
+ *
+ * @param ps
+ * @param values
+ * @param string
+ * @param tag
+ * @param object
+ */
+ private void print(PrintStream ps, Map<String, String> values,
+ String string, String tag, String object) {
+ String value = (String) values.get(string);
+ if (value == null)
+ value = object;
+ if (value == null)
+ return;
+ ps.println(" <" + tag + ">" + value.trim() + "</" + tag + ">");
+ }
+
+ public void close() {
+ super.close();
+ }
+
+ /**
+ * Build Multiple jars. If the -sub command is set, we filter the file with
+ * the given patterns.
+ *
+ * @return
+ * @throws Exception
+ */
+ public Jar[] builds() throws Exception {
+ begin();
+
+ // Are we acting as a conduit for another JAR?
+ String conduit = getProperty(CONDUIT);
+ if (conduit != null) {
+ Map<String, Map<String, String>> map = parseHeader(conduit);
+ Jar[] result = new Jar[map.size()];
+ int n = 0;
+ for (String file : map.keySet()) {
+ Jar c = new Jar(getFile(file));
+ addClose(c);
+ String name = map.get(file).get("name");
+ if (name != null)
+ c.setName(name);
+
+ result[n++] = c;
+ }
+ return result;
+ }
+
+ // If no -sub property, then reuse this builder object
+ // other wise, build all the sub parts.
+ String sub = getProperty(SUB);
+ if (sub == null) {
+ Jar jar = build();
+ if (jar == null)
+ return new Jar[0];
+
+ return new Jar[] { jar };
+ }
+
+ List<Jar> result = new ArrayList<Jar>();
+
+ // Get the Instruction objects that match the sub header
+ Set<Instruction> subs = replaceWitInstruction(parseHeader(sub), SUB)
+ .keySet();
+
+ // Get the member files of this directory
+ List<File> members = new ArrayList<File>(Arrays.asList(getBase()
+ .listFiles()));
+
+ getProperties().remove(SUB);
+ // For each member file
+ nextFile: while (members.size() > 0) {
+
+ File file = members.remove(0);
+ if (file.equals(getPropertiesFile()))
+ continue nextFile;
+
+ for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
+
+ Instruction instruction = i.next();
+ if (instruction.matches(file.getName())) {
+
+ if (!instruction.isNegated()) {
+
+ Builder builder = null;
+ try {
+ builder = getSubBuilder();
+ addClose(builder);
+ builder.setProperties(file);
+ builder.setProperty(SUB, "");
+ // Recursively build
+ // TODO
+ Jar jar = builder.build();
+ jar.setName(builder.getBsn());
+ result.add(jar);
+ } catch (Exception e) {
+ e.printStackTrace();
+ error("Sub Building " + file, e);
+ }
+ if (builder != null)
+ getInfo(builder, file.getName() + ": ");
+ }
+
+ // Because we matched (even though we could be negated)
+ // we skip any remaining searches
+ continue nextFile;
+ }
+ }
+ }
+ setProperty(SUB, sub);
+ return result.toArray(new Jar[result.size()]);
+ }
+
+ public Builder getSubBuilder() throws Exception {
+ Builder builder = new Builder(this);
+ builder.setBase(getBase());
+
+ for (Jar file : getClasspath()) {
+ builder.addClasspath(file);
+ }
+
+ return builder;
+ }
+
+ /**
+ * A macro to convert a maven version to an OSGi version
+ */
+
+ public String _maven_version(String args[]) {
+ if (args.length > 2)
+ error("${maven_version} macro receives too many arguments "
+ + Arrays.toString(args));
+ else if (args.length < 2)
+ error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
+ else {
+ return cleanupVersion(args[1]);
+ }
+ return null;
+ }
+
+ public String _permissions(String args[]) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ for (String arg : args) {
+ if ("packages".equals(arg) || "all".equals(arg)) {
+ for (String imp : getImports().keySet()) {
+ if (!imp.startsWith("java.")) {
+ sb.append("(org.osgi.framework.PackagePermission \"");
+ sb.append(imp);
+ sb.append("\" \"import\")\r\n");
+ }
+ }
+ for (String exp : getExports().keySet()) {
+ sb.append("(org.osgi.framework.PackagePermission \"");
+ sb.append(exp);
+ sb.append("\" \"export\")\r\n");
+ }
+ } else if ("admin".equals(arg) || "all".equals(arg)) {
+ sb.append("(org.osgi.framework.AdminPermission)");
+ } else if ("permissions".equals(arg))
+ ;
+ else
+ error("Invalid option in ${permissions}: %s", arg);
+ }
+ return sb.toString();
+ }
+
+ public void removeBundleSpecificHeaders() {
+ Set<String> set = new HashSet<String>(Arrays
+ .asList(BUNDLE_SPECIFIC_HEADERS));
+ setForceLocal(set);
+ }
+
+}
Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,11 @@
+package aQute.lib.osgi;
+
+public interface ClassDataCollector {
+ void classBegin(int access, String name);
+ void extendsClass(String name);
+ void implementsInterfaces(String name[]);
+ void field(int access, String descriptor);
+ void method(int access, String descriptor);
+ void addReference(String token);
+ void classEnd();
+}
Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/ClassDataCollector.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,832 @@
+/* Copyright 2006 aQute SARL
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+
+public class Clazz {
+ public static enum QUERY {
+ IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION
+ };
+
+ static protected class Assoc {
+ Assoc(byte tag, int a, int b) {
+ this.tag = tag;
+ this.a = a;
+ this.b = b;
+ }
+
+ byte tag;
+ int a;
+ int b;
+ }
+
+ final static byte SkipTable[] = { 0, // 0 non existent
+ -1, // 1 CONSTANT_utf8 UTF 8, handled in
+ // method
+ -1, // 2
+ 4, // 3 CONSTANT_Integer
+ 4, // 4 CONSTANT_Float
+ 8, // 5 CONSTANT_Long (index +=2!)
+ 8, // 6 CONSTANT_Double (index +=2!)
+ -1, // 7 CONSTANT_Class
+ 2, // 8 CONSTANT_String
+ 4, // 9 CONSTANT_FieldRef
+ 4, // 10 CONSTANT_MethodRef
+ 4, // 11 CONSTANT_InterfaceMethodRef
+ 4, // 12 CONSTANT_NameAndType
+ };
+
+ String className;
+ Object pool[];
+ int intPool[];
+ Map<String, Map<String, String>> imports = new HashMap<String, Map<String, String>>();
+ String path;
+
+ // static String type = "([BCDFIJSZ\\[]|L[^<>]+;)";
+ // static Pattern descriptor = Pattern.compile("\\(" + type + "*\\)(("
+ // + type + ")|V)");
+ int minor = 0;
+ int major = 0;
+
+ String sourceFile;
+ Set<String> xref;
+ Set<Integer> classes;
+ Set<Integer> descriptors;
+ int forName = 0;
+ int class$ = 0;
+ String[] interfaces;
+ String zuper;
+ ClassDataCollector cd = new ClassDataCollector() {
+ public void addReference(
+ String token) {
+ }
+
+ public void classBegin(
+ int access,
+ String name) {
+ }
+
+ public void classEnd() {
+ }
+
+ public void extendsClass(
+ String name) {
+ }
+
+ public void field(
+ int access,
+ String descriptor) {
+ }
+
+ public void implementsInterfaces(
+ String[] name) {
+ }
+
+ public void method(
+ int access,
+ String descriptor) {
+ }
+ };
+
+ public Clazz(String path) {
+ this.path = path;
+ }
+
+ public Clazz(String path, InputStream in) throws IOException {
+ this.path = path;
+ DataInputStream din = new DataInputStream(in);
+ parseClassFile(din);
+ din.close();
+ }
+
+ Set<String> parseClassFile(DataInputStream in) throws IOException {
+
+ xref = new HashSet<String>();
+ classes = new HashSet<Integer>();
+ descriptors = new HashSet<Integer>();
+
+ boolean crawl = false; // Crawl the byte code
+ int magic = in.readInt();
+ if (magic != 0xCAFEBABE)
+ throw new IOException("Not a valid class file (no CAFEBABE header)");
+
+ minor = in.readUnsignedShort(); // minor version
+ major = in.readUnsignedShort(); // major version
+ int count = in.readUnsignedShort();
+ pool = new Object[count];
+ intPool = new int[count];
+
+ process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
+ byte tag = in.readByte();
+ switch (tag) {
+ case 0:
+ break process;
+ case 1:
+ constantUtf8(in, poolIndex);
+ break;
+
+ // For some insane optimization reason are
+ // the long and the double two entries in the
+ // constant pool. See 4.4.5
+ case 5:
+ constantLong(in, poolIndex);
+ poolIndex++;
+ break;
+
+ case 6:
+ constantDouble(in, poolIndex);
+ poolIndex++;
+ break;
+
+ case 7:
+ constantClass(in, poolIndex);
+ break;
+
+ case 8:
+ constantString(in, poolIndex);
+ break;
+
+ case 10: // Method ref
+ methodRef(in, poolIndex);
+ break;
+
+ // Name and Type
+ case 12:
+ nameAndType(in, poolIndex, tag);
+ break;
+
+ // We get the skip count for each record type
+ // from the SkipTable. This will also automatically
+ // abort when
+ default:
+ if (tag == 2)
+ throw new IOException("Invalid tag " + tag);
+ in.skipBytes(SkipTable[tag]);
+ break;
+ }
+ }
+
+ pool(pool, intPool);
+ /*
+ * Parse after the constant pool, code thanks to Hans Christian
+ * Falkenberg
+ */
+
+ int access_flags = in.readUnsignedShort(); // access
+ int this_class = in.readUnsignedShort();
+ className = (String) pool[intPool[this_class]];
+ cd.classBegin(access_flags, className);
+
+ try {
+
+ int super_class = in.readUnsignedShort();
+ zuper = (String) pool[intPool[super_class]];
+ if (zuper != null) {
+ addReference(zuper);
+ cd.extendsClass(zuper);
+ }
+
+ int interfacesCount = in.readUnsignedShort();
+ if (interfacesCount > 0) {
+ interfaces = new String[interfacesCount];
+ for (int i = 0; i < interfacesCount; i++)
+ interfaces[i] = (String) pool[intPool[in
+ .readUnsignedShort()]];
+ cd.implementsInterfaces(interfaces);
+ }
+
+ int fieldsCount = in.readUnsignedShort();
+ for (int i = 0; i < fieldsCount; i++) {
+ access_flags = in.readUnsignedShort(); // skip access flags
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+
+ // Java prior to 1.5 used a weird
+ // static variable to hold the com.X.class
+ // result construct. If it did not find it
+ // it would create a variable class$com$X
+ // that would be used to hold the class
+ // object gotten with Class.forName ...
+ // Stupidly, they did not actively use the
+ // class name for the field type, so bnd
+ // would not see a reference. We detect
+ // this case and add an artificial descriptor
+ String name = pool[name_index].toString(); // name_index
+ if (name.startsWith("class$")) {
+ crawl = true;
+ }
+ cd.field(access_flags, pool[descriptor_index].toString());
+ descriptors.add(new Integer(descriptor_index));
+ doAttributes(in, false);
+ }
+
+ //
+ // Check if we have to crawl the code to find
+ // the ldc(_w) <string constant> invokestatic Class.forName
+ // if so, calculate the method ref index so we
+ // can do this efficiently
+ //
+ if (crawl) {
+ forName = findMethod("java/lang/Class", "forName",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ class$ = findMethod(className, "class$",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ }
+
+ //
+ // Handle the methods
+ //
+ int methodCount = in.readUnsignedShort();
+ for (int i = 0; i < methodCount; i++) {
+ access_flags = in.readUnsignedShort();
+ /* int name_index = */in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ // String s = (String) pool[name_index];
+ descriptors.add(new Integer(descriptor_index));
+ if ( pool[descriptor_index ] == null ) {
+ System.out.println("Value in pool=null, descriptor_index=" +descriptor_index);
+ }
+ cd.method(access_flags, pool[descriptor_index].toString());
+ doAttributes(in, crawl);
+ }
+
+ doAttributes(in, false);
+
+ //
+ // Now iterate over all classes we found and
+ // parse those as well. We skip duplicates
+ //
+
+ for (Iterator<Integer> e = classes.iterator(); e.hasNext();) {
+ int class_index = e.next().intValue();
+ doClassReference((String) pool[class_index]);
+ }
+
+ //
+ // Parse all the descriptors we found
+ //
+
+ for (Iterator<Integer> e = descriptors.iterator(); e.hasNext();) {
+ Integer index = e.next();
+ String prototype = (String) pool[index.intValue()];
+ if (prototype != null)
+ parseDescriptor(prototype);
+ else
+ System.err.println("Unrecognized descriptor: " + index);
+ }
+ Set<String> xref = this.xref;
+ reset();
+ return xref;
+ } finally {
+ cd.classEnd();
+ }
+ }
+
+ protected void pool(Object[] pool, int[] intPool) {
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @param tag
+ * @throws IOException
+ */
+ protected void nameAndType(DataInputStream in, int poolIndex, byte tag)
+ throws IOException {
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ descriptors.add(new Integer(descriptor_index));
+ pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @param tag
+ * @throws IOException
+ */
+ private void methodRef(DataInputStream in, int poolIndex)
+ throws IOException {
+ int class_index = in.readUnsignedShort();
+ int name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ private void constantString(DataInputStream in, int poolIndex)
+ throws IOException {
+ int string_index = in.readUnsignedShort();
+ intPool[poolIndex] = string_index;
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ protected void constantClass(DataInputStream in, int poolIndex)
+ throws IOException {
+ int class_index = in.readUnsignedShort();
+ classes.add(new Integer(class_index));
+ intPool[poolIndex] = class_index;
+ }
+
+ /**
+ * @param in
+ * @throws IOException
+ */
+ protected void constantDouble(DataInputStream in, int poolIndex)
+ throws IOException {
+ in.skipBytes(8);
+ }
+
+ /**
+ * @param in
+ * @throws IOException
+ */
+ protected void constantLong(DataInputStream in, int poolIndex)
+ throws IOException {
+ in.skipBytes(8);
+ }
+
+ /**
+ * @param in
+ * @param poolIndex
+ * @throws IOException
+ */
+ protected void constantUtf8(DataInputStream in, int poolIndex)
+ throws IOException {
+ // CONSTANT_Utf8
+
+ String name = in.readUTF();
+ xref.add(name);
+ pool[poolIndex] = name;
+ }
+
+ /**
+ * Find a method reference in the pool that points to the given class,
+ * methodname and descriptor.
+ *
+ * @param clazz
+ * @param methodname
+ * @param descriptor
+ * @return index in constant pool
+ */
+ private int findMethod(String clazz, String methodname, String descriptor) {
+ for (int i = 1; i < pool.length; i++) {
+ if (pool[i] instanceof Assoc) {
+ Assoc methodref = (Assoc) pool[i];
+ if (methodref.tag == 10) {
+ // Method ref
+ int class_index = methodref.a;
+ int class_name_index = intPool[class_index];
+ if (clazz.equals(pool[class_name_index])) {
+ int name_and_type_index = methodref.b;
+ Assoc name_and_type = (Assoc) pool[name_and_type_index];
+ if (name_and_type.tag == 12) {
+ // Name and Type
+ int name_index = name_and_type.a;
+ int type_index = name_and_type.b;
+ if (methodname.equals(pool[name_index])) {
+ if (descriptor.equals(pool[type_index])) {
+ return i;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ private void doClassReference(String next) {
+ if (next != null) {
+ String normalized = normalize(next);
+ if (normalized != null) {
+ cd.addReference(next);
+ String pack = getPackage(normalized);
+ packageReference(pack);
+ }
+ } else
+ throw new IllegalArgumentException("Invalid class, parent=");
+ }
+
+ /**
+ * Called for each attribute in the class, field, or method.
+ *
+ * @param in
+ * The stream
+ * @throws IOException
+ */
+ private void doAttributes(DataInputStream in, boolean crawl)
+ throws IOException {
+ int attributesCount = in.readUnsignedShort();
+ for (int j = 0; j < attributesCount; j++) {
+ // skip name CONSTANT_Utf8 pointer
+ doAttribute(in, crawl);
+ }
+ }
+
+ /**
+ * Process a single attribute, if not recognized, skip it.
+ *
+ * @param in
+ * the data stream
+ * @throws IOException
+ */
+ private void doAttribute(DataInputStream in, boolean crawl)
+ throws IOException {
+ int attribute_name_index = in.readUnsignedShort();
+ String attributeName = (String) pool[attribute_name_index];
+ long attribute_length = in.readInt();
+ attribute_length &= 0xFFFFFFFF;
+ if ("RuntimeVisibleAnnotations".equals(attributeName))
+ doAnnotations(in);
+ else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
+ doParameterAnnotations(in);
+ else if ("SourceFile".equals(attributeName))
+ doSourceFile(in);
+ else if ("Code".equals(attributeName) && crawl)
+ doCode(in);
+// else if ("Signature".equals(attributeName))
+// doSignature(in);
+ else {
+ if (attribute_length > 0x7FFFFFFF) {
+ throw new IllegalArgumentException("Attribute > 2Gb");
+ }
+ in.skipBytes((int) attribute_length);
+ }
+ }
+
+ /**
+ * Handle a signature
+ *
+ * <pre>
+ * Signature_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 signature_index;
+ * }
+ * </pre>
+ */
+
+// void doSignature(DataInputStream in) throws IOException {
+// /*int attribute_length =*/ in.readInt();
+// int signature_index = in.readUnsignedShort();
+// String signature = (String) pool[signature_index];
+// }
+
+ /**
+ * <pre>
+ * Code_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 max_stack;
+ * u2 max_locals;
+ * u4 code_length;
+ * u1 code[code_length];
+ * u2 exception_table_length;
+ * { u2 start_pc;
+ * u2 end_pc;
+ * u2 handler_pc;
+ * u2 catch_type;
+ * } exception_table[exception_table_length];
+ * u2 attributes_count;
+ * attribute_info attributes[attributes_count];
+ * }
+ * </pre>
+ *
+ * @param in
+ * @param pool
+ * @throws IOException
+ */
+ private void doCode(DataInputStream in) throws IOException {
+ /* int max_stack = */in.readUnsignedShort();
+ /* int max_locals = */in.readUnsignedShort();
+ int code_length = in.readInt();
+ byte code[] = new byte[code_length];
+ in.readFully(code);
+ crawl(code);
+ int exception_table_length = in.readUnsignedShort();
+ in.skipBytes(exception_table_length * 8);
+ doAttributes(in, false);
+ }
+
+ /**
+ * We must find Class.forName references ...
+ *
+ * @param code
+ */
+ protected void crawl(byte[] code) {
+ ByteBuffer bb = ByteBuffer.wrap(code);
+ bb.order(ByteOrder.BIG_ENDIAN);
+ int lastReference = -1;
+
+ while (bb.remaining() > 0) {
+ int instruction = 0xFF & bb.get();
+ switch (instruction) {
+ case OpCodes.ldc:
+ lastReference = 0xFF & bb.get();
+ break;
+
+ case OpCodes.ldc_w:
+ lastReference = 0xFFFF & bb.getShort();
+ break;
+
+ case OpCodes.invokestatic:
+ int methodref = 0xFFFF & bb.getShort();
+ if ((methodref == forName || methodref == class$)
+ && lastReference != -1
+ && pool[intPool[lastReference]] instanceof String) {
+ String clazz = (String) pool[intPool[lastReference]];
+ doClassReference(clazz.replace('.', '/'));
+ }
+ break;
+
+ case OpCodes.tableswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ bb.get();
+ /* int deflt = */
+ bb.getInt();
+ int low = bb.getInt();
+ int high = bb.getInt();
+ bb.position(bb.position() + (high - low + 1) * 4);
+ lastReference = -1;
+ break;
+
+ case OpCodes.lookupswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ bb.get();
+ /* deflt = */
+ bb.getInt();
+ int npairs = bb.getInt();
+ bb.position(bb.position() + npairs * 8);
+ lastReference = -1;
+ break;
+
+ default:
+ lastReference = -1;
+ bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
+ }
+ }
+ }
+
+ private void doSourceFile(DataInputStream in) throws IOException {
+ int sourcefile_index = in.readUnsignedShort();
+ this.sourceFile = pool[sourcefile_index].toString();
+ }
+
+ private void doParameterAnnotations(DataInputStream in) throws IOException {
+ int num_parameters = in.readUnsignedByte();
+ for (int p = 0; p < num_parameters; p++) {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ for (int a = 0; a < num_annotations; a++) {
+ doAnnotation(in);
+ }
+ }
+ }
+
+ private void doAnnotations(DataInputStream in) throws IOException {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ for (int a = 0; a < num_annotations; a++) {
+ doAnnotation(in);
+ }
+ }
+
+ private void doAnnotation(DataInputStream in) throws IOException {
+ int type_index = in.readUnsignedShort();
+ descriptors.add(new Integer(type_index));
+ int num_element_value_pairs = in.readUnsignedShort();
+ for (int v = 0; v < num_element_value_pairs; v++) {
+ /* int element_name_index = */in.readUnsignedShort();
+ doElementValue(in);
+ }
+ }
+
+ private void doElementValue(DataInputStream in) throws IOException {
+ int tag = in.readUnsignedByte();
+ switch (tag) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ case 's':
+ /* int const_value_index = */
+ in.readUnsignedShort();
+ break;
+
+ case 'e':
+ int type_name_index = in.readUnsignedShort();
+ descriptors.add(new Integer(type_name_index));
+ /* int const_name_index = */
+ in.readUnsignedShort();
+ break;
+
+ case 'c':
+ int class_info_index = in.readUnsignedShort();
+ descriptors.add(new Integer(class_info_index));
+ break;
+
+ case '@':
+ doAnnotation(in);
+ break;
+
+ case '[':
+ int num_values = in.readUnsignedShort();
+ for (int i = 0; i < num_values; i++) {
+ doElementValue(in);
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "Invalid value for Annotation ElementValue tag " + tag);
+ }
+ }
+
+ void packageReference(String pack) {
+ if (pack.indexOf('<') >= 0)
+ System.out.println("Oops: " + pack);
+ if (!imports.containsKey(pack))
+ imports.put(pack, new LinkedHashMap<String, String>());
+ }
+
+ public void parseDescriptor(String prototype) {
+ if (prototype.startsWith("("))
+ parseMethodDescriptor(prototype);
+ else
+ addReference(prototype);
+ }
+
+ void parseMethodDescriptor(String prototype) {
+ int last = prototype.indexOf(')');
+ if (last < 0)
+ throw new IllegalArgumentException(
+ "Invalid method descriptor in class file: " + className
+ + " " + prototype);
+
+ for (int i = 1; i < last; i++) {
+ if (prototype.charAt(i) == 'L') {
+ int next = prototype.indexOf(';', i);
+ addReference(prototype.substring(i, next));
+ i = next;
+ }
+ }
+ addReference(prototype.substring(last + 1));
+ }
+
+ private void addReference(String token) {
+ cd.addReference(token);
+ while (token.startsWith("["))
+ token = token.substring(1);
+
+ if (token.startsWith("L")) {
+ String clazz = normalize(token.substring(1));
+ if (clazz.startsWith("java/"))
+ return;
+ String pack = getPackage(clazz);
+ packageReference(pack);
+ }
+ }
+
+ static String normalize(String s) {
+ if (s.startsWith("[L"))
+ return normalize(s.substring(2));
+ if (s.startsWith("["))
+ if (s.length() == 2)
+ return null;
+ else
+ return normalize(s.substring(1));
+ if (s.endsWith(";"))
+ return normalize(s.substring(0, s.length() - 1));
+ return s + ".class";
+ }
+
+ public static String getPackage(String clazz) {
+ int n = clazz.lastIndexOf('/');
+ if (n < 0)
+ return ".";
+ return clazz.substring(0, n).replace('/', '.');
+ }
+
+ public Map<String, Map<String, String>> getReferred() {
+ return imports;
+ }
+
+ String getClassName() {
+ return className;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Set<String> xref(InputStream in) throws IOException {
+ DataInputStream din = new DataInputStream(in);
+ Set<String> set = parseClassFile(din);
+ din.close();
+ return set;
+ }
+
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ /**
+ * .class construct for different compilers
+ *
+ * sun 1.1 Detect static variable class$com$acme$MyClass 1.2 " 1.3 " 1.4 "
+ * 1.5 ldc_w (class) 1.6 "
+ *
+ * eclipse 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 "
+ * 1.5 ldc (class) 1.6 "
+ *
+ * 1.5 and later is not an issue, sun pre 1.5 is easy to detect the static
+ * variable that decodes the class name. For eclipse, the class$0 gives away
+ * we have a reference encoded in a string.
+ * compilerversions/compilerversions.jar contains test versions of all
+ * versions/compilers.
+ */
+
+ public void reset() {
+ pool = null;
+ intPool = null;
+ xref = null;
+ classes = null;
+ descriptors = null;
+ }
+
+ public boolean is(QUERY query, Instruction instr,
+ Map<String, Clazz> classspace) {
+ switch (query) {
+ case ANY:
+ return true;
+
+ case NAMED:
+ if (instr.matches(getClassName()))
+ return !instr.isNegated();
+ return false;
+
+ case VERSION:
+ String v = major + "/" + minor;
+ if (instr.matches(v))
+ return !instr.isNegated();
+ return false;
+
+ case IMPLEMENTS:
+ for (int i = 0; interfaces != null && i < interfaces.length; i++) {
+ if (instr.matches(interfaces[i]))
+ return !instr.isNegated();
+ }
+ break;
+ case EXTENDS:
+ if (zuper == null)
+ return false;
+
+ if (instr.matches(zuper))
+ return !instr.isNegated();
+ break;
+
+ case IMPORTS:
+ for (String imp : imports.keySet()) {
+ if (instr.matches(imp.replace('.', '/')))
+ return !instr.isNegated();
+ }
+ }
+
+ if (zuper == null || classspace == null)
+ return false;
+
+ Clazz clazz = classspace.get(zuper);
+ if (clazz == null)
+ return false;
+
+ return clazz.is(query, instr, classspace);
+ }
+
+ public String toString() {
+ return getFQN();
+ }
+
+ public String getFQN() {
+ return getClassName().replace('/', '.');
+ }
+
+ public void setClassDataCollector(ClassDataCollector cd) {
+ this.cd = cd;
+ }
+}
Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Clazz.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,197 @@
+package aQute.lib.osgi;
+
+import java.util.*;
+import java.util.regex.*;
+
+public interface Constants {
+ /*
+ * Defined in OSGi
+ */
+ /**
+ * @syntax Bundle-ActivationPolicy ::= policy ( â;â directive )* policy ::=
+ * âlazyâ
+ */
+ String BUNDLE_ACTIVATION_POLICY = "Bundle-ActivationPolicy";
+ String BUNDLE_ACTIVATOR = "Bundle-Activator";
+ String BUNDLE_BLUEPRINT = "Bundle-Copyright";
+ String BUNDLE_CATEGORY = "Bundle-Category";
+ String BUNDLE_CLASSPATH = "Bundle-ClassPath";
+ String BUNDLE_CONTACTADDRESS = "Bundle-ContactAddress";
+ String BUNDLE_COPYRIGHT = "Bundle-Copyright";
+ String BUNDLE_DESCRIPTION = "Bundle-Description";
+ String BUNDLE_DOCURL = "Bundle-DocURL";
+ String BUNDLE_ICON = "Bundle-Icon";
+ String BUNDLE_LICENSE = "Bundle-License";
+ String BUNDLE_LOCALIZATION = "Bundle-Localization";
+ String BUNDLE_MANIFESTVERSION = "Bundle-ManifestVersion";
+ String BUNDLE_NAME = "Bundle-Name";
+ String BUNDLE_NATIVECODE = "Bundle-NativeCode";
+ String BUNDLE_REQUIREDEXECUTIONENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
+ String BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName";
+ String BUNDLE_UPDATELOCATION = "Bundle-UpdateLocation";
+ String BUNDLE_VENDOR = "Bundle-Vendor";
+ String BUNDLE_VERSION = "Bundle-Version";
+ String DYNAMICIMPORT_PACKAGE = "DynamicImport-Package";
+ String EXPORT_PACKAGE = "Export-Package";
+ String EXPORT_SERVICE = "Export-Service";
+ String FRAGMENT_HOST = "Fragment-Host";
+ String IMPORT_PACKAGE = "Import-Package";
+ String IMPORT_SERVICE = "Import-Service";
+ String REQUIRE_BUNDLE = "Require-Bundle";
+ String SERVICE_COMPONENT = "Service-Component";
+
+ String PRIVATE_PACKAGE = "Private-Package";
+ String IGNORE_PACKAGE = "Ignore-Package";
+ String INCLUDE_RESOURCE = "Include-Resource";
+ String CONDITIONAL_PACKAGE = "Conditional-Package";
+ String BND_LASTMODIFIED = "Bnd-LastModified";
+ String CREATED_BY = "Created-By";
+ String TOOL = "Tool";
+ String TESTCASES = "Test-Cases";
+
+ String headers[] = {
+ BUNDLE_ACTIVATOR, BUNDLE_CONTACTADDRESS, BUNDLE_COPYRIGHT,
+ BUNDLE_DESCRIPTION, BUNDLE_DOCURL, BUNDLE_LOCALIZATION,
+ BUNDLE_NATIVECODE, BUNDLE_VENDOR, BUNDLE_VERSION, BUNDLE_LICENSE,
+ BUNDLE_CLASSPATH, SERVICE_COMPONENT, EXPORT_PACKAGE,
+ IMPORT_PACKAGE, BUNDLE_LOCALIZATION, BUNDLE_MANIFESTVERSION,
+ BUNDLE_NAME, BUNDLE_NATIVECODE,
+ BUNDLE_REQUIREDEXECUTIONENVIRONMENT, BUNDLE_SYMBOLICNAME,
+ BUNDLE_VERSION, FRAGMENT_HOST, PRIVATE_PACKAGE, IGNORE_PACKAGE,
+ INCLUDE_RESOURCE, REQUIRE_BUNDLE, IMPORT_SERVICE, EXPORT_SERVICE,
+ CONDITIONAL_PACKAGE, BND_LASTMODIFIED, TESTCASES };
+
+ String BUILDPATH = "-buildpath";
+ String BUMPPOLICY = "-bumppolicy";
+ String CONDUIT = "-conduit";
+ String DEPENDSON = "-dependson";
+ String DEPLOYREPO = "-deployrepo";
+ String DONOTCOPY = "-donotcopy";
+ String DEBUG = "-debug";
+ String EXPORT_CONTENTS = "-exportcontents";
+ String FAIL_OK = "-failok";
+ String INCLUDE = "-include";
+ String INCLUDERESOURCE = "-includeresource";
+ String MAKE = "-make";
+ String MANIFEST = "-manifest";
+ String NOEXTRAHEADERS = "-noextraheaders";
+ String NOMANIFEST = "-nomanifest";
+ String NOUSES = "-nouses";
+ String NOPE = "-nope";
+ String PEDANTIC = "-pedantic";
+ String PLUGIN = "-plugin";
+ String POM = "-pom";
+ String RELEASEREPO = "-releaserepo";
+ String REMOVE_HEADERS = "-removeheaders";
+ String RESOURCEONLY = "-resourceonly";
+ String SOURCES = "-sources";
+ String SOURCEPATH = "-sourcepath";
+ String SUB = "-sub";
+ String RUNPROPERTIES = "-runproperties";
+ String RUNSYSTEMPACKAGES = "-runsystempackages";
+ String RUNBUNDLES = "-runbundles";
+ String RUNPATH = "-runpath";
+ String RUNVM = "-runvm";
+
+ String REPORTNEWER = "-reportnewer";
+ String SIGN = "-sign";
+ String TESTPACKAGES = "-testpackages";
+ String TESTREPORT = "-testreport";
+ String TESTBUNDLES = "-testbundles";
+ String UNDERTEST = "-undertest";
+ String VERBOSE = "-verbose";
+ String VERSIONPOLICY = "-versionpolicy";
+
+ // Deprecated
+ String CLASSPATH = "-classpath";
+
+ String options[] = {
+ BUILDPATH, BUMPPOLICY, CONDUIT, CLASSPATH, DEPENDSON, DONOTCOPY,
+ EXPORT_CONTENTS, FAIL_OK, INCLUDE, INCLUDERESOURCE, MAKE, MANIFEST,
+ NOEXTRAHEADERS, NOUSES, NOPE, PEDANTIC, PLUGIN, POM,
+ REMOVE_HEADERS, RESOURCEONLY, SOURCES, SOURCEPATH, SOURCES,
+ SOURCEPATH, SUB, RUNBUNDLES, RUNPATH, RUNSYSTEMPACKAGES,
+ RUNPROPERTIES, REPORTNEWER, UNDERTEST, TESTBUNDLES, TESTPACKAGES,
+ TESTREPORT, VERBOSE, NOMANIFEST, DEPLOYREPO, RELEASEREPO };
+
+ // Ignore bundle specific headers. These bundles do not make
+ // a lot of sense to inherit
+ String[] BUNDLE_SPECIFIC_HEADERS = new String[] {
+ INCLUDE_RESOURCE, BUNDLE_ACTIVATOR, BUNDLE_CLASSPATH, BUNDLE_NAME,
+ BUNDLE_NATIVECODE, BUNDLE_SYMBOLICNAME, IMPORT_PACKAGE,
+ EXPORT_PACKAGE, DYNAMICIMPORT_PACKAGE, FRAGMENT_HOST,
+ REQUIRE_BUNDLE, PRIVATE_PACKAGE, EXPORT_CONTENTS, TESTCASES,
+ NOMANIFEST };
+
+ char DUPLICATE_MARKER = '~';
+
+ String SPLIT_PACKAGE_DIRECTIVE = "-split-package:";
+ String IMPORT_DIRECTIVE = "-import:";
+ String NO_IMPORT_DIRECTIVE = "-noimport:";
+ String REMOVE_ATTRIBUTE_DIRECTIVE = "-remove-attribute:";
+
+ String USES_DIRECTIVE = "uses:";
+ String MANDATORY_DIRECTIVE = "mandatory:";
+ String INCLUDE_DIRECTIVE = "include:";
+ String EXCLUDE_DIRECTIVE = "exclude:";
+ String PRESENCE_DIRECTIVE = "presence:";
+ String SINGLETON_DIRECTIVE = "singleton:";
+ String EXTENSION_DIRECTIVE = "extension:";
+ String VISIBILITY_DIRECTIVE = "visibility:";
+ String FRAGMENT_ATTACHMENT_DIRECTIVE = "fragment-attachment:";
+ String RESOLUTION_DIRECTIVE = "resolution:";
+ String PATH_DIRECTIVE = "path:";
+ String SIZE_ATTRIBUTE = "size";
+ String LINK_ATTRIBUTE = "link";
+ String NAME_ATTRIBUTE = "name";
+ String DESCRIPTION_ATTRIBUTE = "description";
+ String OSNAME_ATTRIBUTE = "osname";
+ String OSVERSION_ATTRIBUTE = "osversion";
+ String PROCESSOR_ATTRIBUTE = "processor";
+ String LANGUAGE_ATTRIBUTE = "language";
+ String SELECTION_FILTER_ATTRIBUTE = "selection-filter";
+ String BLUEPRINT_WAIT_FOR_DEPENDENCIES_ATTRIBUTE = "blueprint.wait-for-dependencies";
+ String BLUEPRINT_TIMEOUT_ATTRIBUTE = "blueprint.timeout";
+ String VERSION_ATTRIBUTE = "version";
+ String BUNDLE_SYMBOLIC_NAME_ATTRIBUTE = "bundle-symbolic-name";
+ String BUNDLE_VERSION_ATTRIBUTE = "bundle-version";
+
+ String KEYSTORE_LOCATION_DIRECTIVE = "keystore:";
+ String KEYSTORE_PROVIDER_DIRECTIVE = "provider:";
+ String KEYSTORE_PASSWORD_DIRECTIVE = "password:";
+ String SIGN_PASSWORD_DIRECTIVE = "sign-password:";
+
+ String NONE = "none";
+
+ String directives[] = {
+ SPLIT_PACKAGE_DIRECTIVE, NO_IMPORT_DIRECTIVE, IMPORT_DIRECTIVE,
+ RESOLUTION_DIRECTIVE, INCLUDE_DIRECTIVE, USES_DIRECTIVE,
+ EXCLUDE_DIRECTIVE, KEYSTORE_LOCATION_DIRECTIVE,
+ KEYSTORE_PROVIDER_DIRECTIVE, KEYSTORE_PASSWORD_DIRECTIVE,
+ SIGN_PASSWORD_DIRECTIVE,
+
+ // TODO
+ };
+
+ String USES_USES = "<<USES>>";
+ String CURRENT_USES = "@uses";
+ String IMPORT_REFERENCE = "reference";
+ String IMPORT_PRIVATE = "private";
+ String[] importDirectives = {
+ IMPORT_REFERENCE, IMPORT_PRIVATE };
+
+ static final Pattern VALID_PROPERTY_TYPES = Pattern
+ .compile("(String|Long|Double|Float|Integer|Byte|Character|Boolean|Short)");
+
+ String DEFAULT_BND_EXTENSION = ".bnd";
+ String DEFAULT_JAR_EXTENSION = ".jar";
+ String DEFAULT_BAR_EXTENSION = ".bar";
+ String[] METAPACKAGES = {
+ "META-INF", "OSGI-INF", "OSGI-OPT" };
+
+ int STRATEGY_HIGHEST = 1;
+ int STRATEGY_LOWEST = -1;
+
+ String CURRENT_VERSION = "@";
+ String CURRENT_PACKAGE = "@package";
+}
Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Constants.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java?rev=793527&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java Mon Jul 13 10:06:47 2009
@@ -0,0 +1,88 @@
+/* Copyright 2006 aQute SARL
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.zip.*;
+
+public class EmbeddedResource implements Resource {
+ byte data[];
+ long lastModified;
+ String extra;
+
+ public EmbeddedResource(byte data[], long lastModified) {
+ this.data = data;
+ this.lastModified = lastModified;
+ }
+
+ public InputStream openInputStream() throws FileNotFoundException {
+ return new ByteArrayInputStream(data);
+ }
+
+ public void write(OutputStream out) throws IOException {
+ out.write(data);
+ }
+
+ public String toString() {
+ return ":" + data.length + ":";
+ }
+
+ public static void build(Jar jar, InputStream in, long lastModified) throws IOException {
+ ZipInputStream jin = new ZipInputStream(in);
+ ZipEntry entry = jin.getNextEntry();
+ while (entry != null) {
+ if (!entry.isDirectory()) {
+ byte data[] = collect(jin);
+ jar.putResource(entry.getName(), new EmbeddedResource(data, lastModified), true);
+ }
+ entry = jin.getNextEntry();
+ }
+ jin.close();
+ }
+
+ /**
+ * Convenience method to turn an inputstream into a byte array. The method
+ * uses a recursive algorithm to minimize memory usage.
+ *
+ * @param in stream with data
+ * @param offset where we are in the stream
+ * @returns byte array filled with data
+ */
+ static byte[] collect(InputStream in) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copy(in,out);
+ return out.toByteArray();
+ }
+
+ static void copy(InputStream in, OutputStream out) throws IOException {
+ int available = in.available();
+ if ( available <= 10000)
+ available = 64000;
+ byte [] buffer = new byte[available];
+ int size;
+ while ( (size=in.read(buffer))>0)
+ out.write(buffer,0,size);
+ }
+
+ public long lastModified() {
+ return lastModified;
+ }
+
+ public static void build(Jar sub, Resource resource) throws IOException {
+ InputStream in = resource.openInputStream();
+ build(sub,in, resource.lastModified());
+ in.close();
+ }
+
+ public String getExtra() {
+ return extra;
+ }
+
+ public void setExtra(String extra) {
+ this.extra = extra;
+ }
+
+ public long size() {
+ return data.length;
+ }
+}
Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/EmbeddedResource.java
------------------------------------------------------------------------------
svn:eol-style = native