Package aQute.lib.osgi

Source Code of aQute.lib.osgi.Builder

package aQute.lib.osgi;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Manifest;
import java.util.zip.ZipException;

import aQute.bnd.make.Make;
import aQute.bnd.maven.PomResource;
import aQute.bnd.service.SignerPlugin;

/**
* 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.27 $
*/
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;

  List<File> sourcePath = new ArrayList<File>();

  Make make = new Make(this);

  public Builder(Processor parent) {
    super(parent);
  }

  public Builder() {
  }

  public Jar build() throws Exception {
    init();
    if (isTrue(getProperty(NOBUNDLES)))
      return null;

    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);

    dot = doWab(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.setDoNotTouchManifest();

    // This must happen after we analyzed so
    // we know what it is on the classpath
    addSources(dot);

    if (getProperty(POM) != null)
      dot.putResource("pom.xml", new PomResource(dot.getManifest()));

    if (!isNoBundle())
      doVerify(dot);

    if (dot.getResources().isEmpty())
      error("The JAR is empty: " + dot.getName());

    dot.updateModified(lastModified(), "Last Modified Processor");
    dot.setName(getBsn());

    sign(dot);

    doSaveManifest(dot);
    return dot;
  }

  /**
   * Allow any local initialization by subclasses before we build. Default is
   * do nothing.
   */
  public void init() throws Exception {

  }

  /**
   * Turn this normal bundle in a web and add any resources.
   *
   * @throws Exception
   */
  private Jar doWab(Jar dot) throws Exception {
    String wab = getProperty(WAB);
    String wablib = getProperty(WABLIB);
    if (wab == null && wablib == null)
      return dot;

    setProperty(BUNDLE_CLASSPATH, append("WEB-INF/classes",
        getProperty(BUNDLE_CLASSPATH)));

    Jar next = new Jar(dot.getName());
    addClose(next);

    for (Map.Entry<String, Resource> entry : dot.getResources().entrySet()) {
      String path = entry.getKey();
      if (path.indexOf('/') > 0 && !Character.isUpperCase(path.charAt(0))) {
        trace("wab: moving: %s", path);
        next.putResource("WEB-INF/classes/" + path, entry.getValue());
      } else {
        trace("wab: not moving: %s", path);
        next.putResource(path, entry.getValue());
      }
    }

    Map<String, Map<String, String>> clauses = parseHeader(getProperty(WABLIB));
    for (String key : clauses.keySet()) {
      File f = getFile(key);
      addWabLib(next, f);
    }
    doIncludeResource(next, wab);
    return next;
  }

  /**
   * Add a wab lib to the jar.
   *
   * @param f
   */
  private void addWabLib(Jar dot, File f) throws Exception {
    if (f.exists()) {
      Jar jar = new Jar(f);
      jar.setDoNotTouchManifest();
      addClose(jar);
      String path = "WEB-INF/lib/" + f.getName();
      dot.putResource(path, new JarResource(jar));
      setProperty(BUNDLE_CLASSPATH, append(getProperty(BUNDLE_CLASSPATH),
          path));

      Manifest m = jar.getManifest();
      String cp = m.getMainAttributes().getValue("Class-Path");
      if (cp != null) {
        Collection<String> parts = split(cp, ",");
        for (String part : parts) {
          File sub = getFile(f.getParentFile(), part);
          if (!sub.exists()
              || !sub.getParentFile().equals(f.getParentFile())) {
            warning(
                "Invalid Class-Path entry %s in %s, must exist and must reside in same directory",
                sub, f);
          } else {
            addWabLib(dot, sub);
          }
        }
      }
    } else {
      error("WAB lib does not exist %s", f);
    }
  }

  /**
   * Get the manifest and write it out separately if -savemanifest is set
   *
   * @param dot
   */
  private void doSaveManifest(Jar dot) throws IOException {
    String output = getProperty(SAVEMANIFEST);
    if (output == null)
      return;

    File f = getFile(output);
    if (f.isDirectory()) {
      f = new File(f, "MANIFEST.MF");
    }
    f.delete();
    f.getParentFile().mkdirs();
    OutputStream out = new FileOutputStream(f);
    try {
      Jar.writeManifest(dot.getManifest(), out);
    } finally {
      out.close();
    }
    changedFile(f);
  }

  protected void changedFile(File f) {
  }

  /**
   * 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 Exception {
    Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
    if (conditionals.isEmpty())
      return;

    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", Instruction
          .replaceWithInstruction(filtered), 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 Exception {
    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 = Instruction
        .replaceWithInstruction(getHeader(PRIVATE_PACKAGE));
    Map<Instruction, Map<String, String>> exportMap = Instruction
        .replaceWithInstruction(getHeader(EXPORT_PACKAGE));

    if (isTrue(getProperty(Constants.UNDERTEST))) {
      privateMap
          .putAll(Instruction
              .replaceWithInstruction(parseHeader(getProperty(
                  Constants.TESTPACKAGES,
                  "test;presence:=optional"))));
    }
    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 (!isNoBundle()) {
      if (privateMap.isEmpty() && exportMap.isEmpty()
          && !isResourceOnly()
          && getProperty(EXPORT_CONTENTS) == null) {
        warning("None of Export-Package, Private-Package, -testpackages, or -exportcontents 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) {
      StringBuilder sb = new StringBuilder();
      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.toString());
        del = "\n                ";
      }
      sb.append("\nClasspath: ");
      sb.append(Processor.join(getClasspath()));
      sb.append("\n");

      warning(sb.toString());
      if (isPedantic())
        diagnostics = true;
    }
  }

  /**
   * 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.keySet(), 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 Instruction matches(Collection<Instruction> instructions,
      String pack, Set<Instruction> superfluousPatterns) {
    for (Instruction pattern : instructions) {
      if (pattern.matches(pack)) {
        if (superfluousPatterns != null)
          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.length() == 0)
        includes = getProperty("Include-Resource");
    } else
      warning("Please use -includeresource instead of Bundle-Includes");

    doIncludeResource(jar, includes);

  }

  private void doIncludeResource(Jar jar, String includes) throws Exception {
    Map<String, Map<String, String>> clauses = parseHeader(includes);
    doIncludeResource(jar, clauses);
  }

  private void doIncludeResource(Jar jar,
      Map<String, Map<String, String>> clauses) throws ZipException,
      IOException, Exception {
    for (Map.Entry<String, Map<String, String>> entry : clauses.entrySet()) {
      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();
    }

    String parts[] = name.split("\\s*=\\s*");
    String source = parts[0];
    String destination = parts[0];
    if (parts.length == 2)
      source = parts[1];

    if (source.startsWith("@")) {
      extractFromJar(jar, source.substring(1), parts.length == 1 ? ""
          : destination);
    } else 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 {
      File sourceFile;
      String destinationPath;

      sourceFile = getFile(source);
      if (parts.length == 1) {
        // Directories should be copied to the root
        // but files to their file name ...
        if (sourceFile.isDirectory())
          destinationPath = "";
        else
          destinationPath = sourceFile.getName();
      } else {
        destinationPath = parts[0];
      }
      // Handle directories
      if (sourceFile.isDirectory()) {
        destinationPath = doResourceDirectory(jar, extra, preprocess,
            sourceFile, destinationPath);
        return;
      }

      // destinationPath = checkDestinationPath(destinationPath);

      if (!sourceFile.exists()) {
        noSuchFile(jar, name, extra, source, destinationPath);
      } else
        copy(jar, destinationPath, sourceFile, preprocess, extra);
    }
  }

  private String doResourceDirectory(Jar jar, Map<String, String> extra,
      boolean preprocess, File sourceFile, String destinationPath)
      throws Exception {
    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);
    }

    Map<String, File> files = newMap();
    resolveFiles(sourceFile, iFilter, recursive, destinationPath, files,
        flatten);

    for (Map.Entry<String, File> entry : files.entrySet()) {
      copy(jar, entry.getKey(), entry.getValue(), preprocess, extra);
    }
    return destinationPath;
  }

  private void resolveFiles(File dir, FileFilter filter, boolean recursive,
      String path, Map<String, File> files, boolean flatten) {

    if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
      return;
    }

    File[] fs = dir.listFiles(filter);
    for (File file : fs) {
      if (file.isDirectory()) {
        if (recursive) {
          String nextPath;
          if (flatten)
            nextPath = path;
          else
            nextPath = appendPath(path, file.getName());

          resolveFiles(file, filter, recursive, nextPath, files,
              flatten);
        }
        // Directories are ignored otherwise
      } else {
        String p = appendPath(path, file.getName());
        if (files.containsKey(p))
          warning(
              "Include-Resource overwrites entry %s from file %s",
              p, file);
        files.put(p, file);
      }
    }
  }

  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 source, String destination)
      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 = source.lastIndexOf("!/");
    Instruction instr = null;
    if (n > 0) {
      instr = Instruction.getPattern(source.substring(n + 2));
      source = source.substring(0, n);
    }

    // Pattern filter = null;
    // if (n > 0) {
    // String fstring = source.substring(n + 2);
    // source = source.substring(0, n);
    // filter = wildcard(fstring);
    // }
    Jar sub = getJarFromName(source, "extract from jar");
    if (sub == null)
      error("Can not find JAR file " + source);
    else {
      jar.addAll(sub, instr, destination);
    }
  }

  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()) {

      File files[] = from.listFiles();
      for (int i = 0; i < files.length; i++) {
        copy(jar, appendPath(path, 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);
        if (path.endsWith("/"))
          path = path + from.getName();
        jar.putResource(path, resource);

        if (isTrue(extra.get(LIB_DIRECTIVE))) {
          setProperty(BUNDLE_CLASSPATH, append(
              getProperty(BUNDLE_CLASSPATH), path));
        }
      } 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);
  }

  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;
    }

    List<Jar> result = new ArrayList<Jar>();
    List<Builder> builders;

    builders = getSubBuilders();

    for (Builder builder : builders) {
      try {
        Jar jar = builder.build();
        jar.setName(builder.getBsn());
        result.add(jar);
      } catch (Exception e) {
        error("Sub Building " + builder.getBsn(), e);
      }
      if (builder != this)
        getInfo(builder, builder.getBsn() + ": ");
    }
    return result.toArray(new Jar[result.size()]);
  }

  /**
   * Answer a list of builders that represent this file or a list of files
   * specified in -sub. This list can be empty. These builders represents to
   * be created artifacts and are each scoped to such an artifacts. The
   * builders can be used to build the bundles or they can be used to find out
   * information about the to be generated bundles.
   *
   * @return List of 0..n builders representing artifacts.
   * @throws Exception
   */
  public List<Builder> getSubBuilders() throws Exception {
    String sub = (String) getProperty(SUB);
    if (sub == null || sub.trim().length() == 0 || EMPTY_HEADER.equals(sub))
      return Arrays.asList(this);

    List<Builder> builders = new ArrayList<Builder>();
    if (isTrue(getProperty(NOBUNDLES)))
      return builders;

    Set<Instruction> subs = Instruction.replaceWithInstruction(
        parseHeader(sub)).keySet();

    List<File> members = new ArrayList<File>(Arrays.asList(getBase()
        .listFiles()));

    nextFile: while (members.size() > 0) {

      File file = members.remove(0);

      // Check if the file is one of our parents
      Processor p = this;
      while ( p != null ) {
        if ( file.equals(p.getPropertiesFile()))
          continue nextFile;
        p = p.getParent();       
      }
     
      // if
      // (file.getCanonicalFile().equals(getPropertiesFile().getCanonicalFile()))
      // continue nextFile;

      for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {

        Instruction instruction = i.next();
        if (instruction.matches(file.getName())) {

          if (!instruction.isNegated()) {

            Builder builder = getSubBuilder();
            if (builder != null) {
              builder.setProperties(file);
              addClose(builder);
              builders.add(builder);
            }
          }

          // Because we matched (even though we could be negated)
          // we skip any remaining searches
          continue nextFile;
        }
      }
    }
    return builders;
  }

  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);
  }

  /**
   * Check if the given resource is in scope of this bundle. That is, it
   * checks if the Include-Resource includes this resource or if it is a class
   * file it is on the class path and the Export-Pacakge or Private-Package
   * include this resource.
   *
   * For now, include resources are skipped.
   *
   * @param f
   * @return
   */
  public boolean isInScope(Collection<File> resources) throws Exception {
    Map<String, Map<String, String>> clauses = parseHeader(getProperty(Constants.EXPORT_PACKAGE));
    clauses.putAll(parseHeader(getProperty(Constants.PRIVATE_PACKAGE)));
    if (isTrue(getProperty(Constants.UNDERTEST))) {
      clauses.putAll(parseHeader(getProperty(Constants.TESTPACKAGES,
          "test;presence:=optional")));
    }
    Collection<Instruction> instructions = Instruction
        .replaceWithInstruction(clauses).keySet();

    for (File r : resources) {
      String cpEntry = getClasspathEntrySuffix(r);
      if (cpEntry != null) {
        String pack = Clazz.getPackage(cpEntry);
        Instruction i = matches(instructions, pack, null);
        if (i != null)
          return !i.isNegated();
      }
    }
    return false;
  }

  /**
   * Answer the string of the resource that it has in the container.
   *
   * @param resource
   *            The resource to look for
   * @return
   * @throws Exception
   */
  public String getClasspathEntrySuffix(File resource) throws Exception {
    for (Jar jar : getClasspath()) {
      File source = jar.getSource();
      if (source != null) {
        source = source.getCanonicalFile();
        String sourcePath = source.getAbsolutePath();
        String resourcePath = resource.getAbsolutePath();

        if (resourcePath.startsWith(sourcePath)) {
          return resourcePath.substring(sourcePath.length() + 1);
        }
      }
    }
    return null;
  }

}
TOP

Related Classes of aQute.lib.osgi.Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.