Package aQute.bnd.main

Source Code of aQute.bnd.main.bnd

package aQute.bnd.main;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import java.util.prefs.*;
import java.util.regex.*;
import java.util.zip.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import javax.xml.xpath.*;

import org.w3c.dom.*;

import aQute.bnd.build.*;
import aQute.bnd.maven.*;
import aQute.bnd.maven.support.*;
import aQute.bnd.service.*;
import aQute.bnd.service.RepositoryPlugin.*;
import aQute.bnd.service.action.*;
import aQute.bnd.settings.*;
import aQute.lib.deployer.*;
import aQute.lib.io.*;
import aQute.lib.jardiff.*;
import aQute.lib.osgi.*;
import aQute.lib.osgi.eclipse.*;
import aQute.lib.tag.*;
import aQute.libg.generics.*;
import aQute.libg.version.*;


/**
* Utility to make bundles.
*
* TODO Add Javadoc comment for this type.
*
* @version $Revision: 1.14 $
*/
public class bnd extends Processor {
  Settings    settings  = new Settings();
  PrintStream    out      = System.out;
  static boolean  exceptions  = false;

  static boolean  failok    = false;
  private Project  project;

  public static void main(String args[]) {
    bnd main = new bnd();

    try {
      main.run(args);
      if (bnd.failok)
        return;

      System.exit(main.getErrors().size());

    } catch (Exception e) {
      System.err.println("Software error occurred " + e);
      if (exceptions)
        e.printStackTrace();
    }
    System.exit(-1);
  }

  void run(String[] args) throws Exception {
    int i = 0;

    try {
      for (; i < args.length; i++) {
        if ("-failok".equals(args[i])) {
          failok = true;
        } else if ("-exceptions".equals(args[i])) {
          exceptions = true;
        } else if ("-trace".equals(args[i])) {
          setTrace(true);
        } else if ("-pedantic".equals(args[i])) {
          setPedantic(true);
        } else if (args[i].indexOf('=') > 0) {
          String parts[] = args[i].split("\\s*(?!\\\\)=\\s*");
          if (parts.length == 2)
            setProperty(parts[0], parts[1]);
          else
            error("invalid property def: %s", args[i]);
        } else if ("-base".equals(args[i])) {
          setBase(new File(args[++i]).getAbsoluteFile());
          if (!getBase().isDirectory()) {
            out.println("-base must be a valid directory");
          } else if (args[i].startsWith("-"))
            error("Invalid option: ", args[i]);
        } else
          break;
      }

      project = getProject();
      if (project != null) {
        setParent(project);
        project.setPedantic(isPedantic());
        project.setTrace(isTrace());
      }
      trace("project = %s", project);

      if (i >= args.length) {
        if (project != null && project.isValid()) {
          trace("default build of current project");
          project.build();
        } else
          doHelp();
      } else {
        if (!doProject(project, args, i)) {
          if (!doCommand(args, i)) {
            doFiles(args, i);
          }
        }
      }
    } catch (Throwable t) {
      if (exceptions)
        t.printStackTrace();
      error("exception %s", t, t);
    }

    int n = 1;
    switch (getErrors().size()) {
    case 0:
      // System.err.println("No errors");
      break;
    case 1:
      System.err.println("One error");
      break;
    default:
      System.err.println(getErrors().size() + " errors");
    }
    for (String msg : getErrors()) {
      System.err.println(n++ + " : " + msg);
    }
    n = 1;
    switch (getWarnings().size()) {
    case 0:
      // System.err.println("No warnings");
      break;
    case 1:
      System.err.println("One warning");
      break;
    default:
      System.err.println(getWarnings().size() + " warnings");
    }
    for (String msg : getWarnings()) {
      System.err.println(n++ + " : " + msg);
    }

    if (getErrors().size() != 0) {
      System.err.flush();
      System.out.flush();
      Thread.sleep(1000);
      System.exit(getErrors().size());
    }
  }

  boolean doProject(Project project, String[] args, int i) throws Exception {
    if (project != null) {
      trace("project command %s", args[i]);
      Action a = project.getActions().get(args[i]);
      if (a != null) {
        a.execute(project, args[i++]);
        getInfo(project);
        return true;
      }
    }
    return false;
  }

  boolean doCommand(String args[], int i) throws Exception {
    String cmd = args[i];
    trace("command %s", cmd);
    if ("wrap".equals(args[i])) {
      doWrap(args, ++i);
    } else if ("maven".equals(args[i])) {
      MavenCommand maven = new MavenCommand(this);
      maven.setTrace(isTrace());
      maven.setPedantic(isPedantic());
      maven.run(args, ++i);
      getInfo(maven);
    } else if ("global".equals(args[i])) {
      global(args, ++i);
    } else if ("exec".equals(args[i])) {
      doRun(args[++i]);
    } else if ("print".equals(args[i])) {
      doPrint(args, ++i);
    } else if ("lib".equals(args[i])) {
      doLib(args, ++i);
    } else if ("graph".equals(args[i])) {
      doDot(args, ++i);
    } else if ("create-repo".equals(args[i])) {
      createRepo(args, ++i);
    } else if ("release".equals(args[i])) {
      doRelease(args, ++i);
    } else if ("debug".equals(args[i])) {
      debug(args, ++i);
    } else if ("bump".equals(args[i])) {
      bump(args, ++i);
    } else if ("deliverables".equals(args[i])) {
      deliverables(args, ++i);
    } else if ("view".equals(args[i])) {
      doView(args, ++i);
    } else if ("buildx".equals(args[i])) {
      doBuild(args, ++i);
    } else if ("extract".equals(args[i])) {
      doExtract(args, ++i);
    } else if ("patch".equals(args[i])) {
      patch(args, ++i);
    } else if ("runtests".equals(args[i])) {
      runtests(args, ++i);
    } else if ("xref".equals(args[i])) {
      doXref(args, ++i);
    } else if ("eclipse".equals(args[i])) {
      doEclipse(args, ++i);
    } else if ("repo".equals(args[i])) {
      repo(args, ++i);
    } else if ("diff".equals(args[i])) {
      doDiff(args, ++i);
    } else if ("help".equals(args[i])) {
      doHelp(args, ++i);
    } else if ("macro".equals(args[i])) {
      doMacro(args, ++i);
    } else if ("multibuild".equals(args[i])) {
      doMulti(args, ++i);
    } else if ("merge".equals(args[i])) {
      doMerge(args, ++i);
    } else {
      trace("command %s not found", cmd);
      return false;
    }

    trace("command %s executed", cmd);
    return true;
  }

  private void doMulti(String[] args, int i) throws Exception {
    // Project p = getProject();
    // Workspace workspace;
    //
    // if (p != null)
    // workspace = p.getWorkspace();
    // else
    // workspace = Workspace.getWorkspace(getBase());
    //
    // trace("Starting multibuild");
    // MultiBuilder multiBuilder = new MultiBuilder(workspace);
    // multiBuilder.startBuild();
    //
    // trace("Syncing multibuild");
    // multiBuilder.syncBuild();
    // trace("Synced");
    // if ( p != null) {
    // trace("Build %s", (Object) p.build());
    // }
  }

  boolean doFiles(String args[], int i) throws Exception {
    while (i < args.length) {
      String path = args[i];
      if (path.endsWith(Constants.DEFAULT_BND_EXTENSION))
        doBuild(getFile(path), new File[0], new File[0], null, "",
            new File(path).getParentFile(), 0, new HashSet<File>());
      else if (path.endsWith(Constants.DEFAULT_JAR_EXTENSION)
          || path.endsWith(Constants.DEFAULT_BAR_EXTENSION))
        doPrint(path, -1);
      else if (path.endsWith(Constants.DEFAULT_BNDRUN_EXTENSION))
        doRun(path);
      else
        error("Unknown file %s", path);
      i++;
    }
    return true;
  }

  private void doRun(String path) throws Exception {
    File file = getFile(path);
    if (!file.isFile())
      throw new FileNotFoundException(path);

    File projectDir = file.getParentFile();
    File workspaceDir = projectDir.getParentFile();
    if (workspaceDir == null) {
      workspaceDir = new File(System.getProperty("user.home") + File.separator + ".bnd");
    }
    Workspace ws = Workspace.getWorkspace(workspaceDir);

    File bndbnd = new File(projectDir, Project.BNDFILE);
    Project project;
    if (bndbnd.isFile()) {
      project = new Project(ws, projectDir, bndbnd);
      project.doIncludeFile(file, true, project.getProperties());
    } else
      project = new Project(ws, projectDir, file);

    project.setTrace(isTrace());
    project.setPedantic(isPedantic());
    try {
      project.run();

    } catch (Exception e) {
      error("Failed to run %s: %s", project, e);
    }
    getInfo(project);
  }

  private void bump(String[] args, int i) throws Exception {
    if (getProject() == null) {
      error("No project found, use -base <dir> bump");
      return;
    }

    String mask = null;
    if (args.length > i) {
      mask = args[i];
      if (mask.equalsIgnoreCase("major"))
        mask = "+00";
      else if (mask.equalsIgnoreCase("minor"))
        mask = "=+0";
      else if (mask.equalsIgnoreCase("micro"))
        mask = "==+";
      else if (!mask.matches("(+=0){1,3}")) {
        error("Invalid mask for version bump %s, is (minor|major|micro|<mask>), see $version for mask",
            mask);
        return;
      }
    }
    if (mask == null)
      getProject().bump();
    else
      getProject().bump(mask);

    out.println(getProject().getProperty(BUNDLE_VERSION, "No version found"));
  }

  /**
   * Take the current project, calculate its dependencies from the repo and
   * create a mini-repo from it that can easily be unzipped.
   *
   * @param args
   * @param i
   * @throws Exception
   */
  private void createRepo(String[] args, int i) throws Exception {
    Project project = getProject();
    if (project == null) {
      error("not in a proper bnd project ... " + getBase());
      return;
    }

    File out = getFile(project.getName() + ".repo.jar");
    List<Instruction> skip = Create.list();

    while (i < args.length) {
      if (args[i].equals("-o")) {
        i++;
        if (i < args.length)
          out = getFile(args[i]);
        else
          error("No arg for -out");
      } else if (args[i].equals("-skip")) {
        i++;
        if (i < args.length) {
          Instruction instr = Instruction.getPattern(args[i]);
          skip.add(instr);
        } else
          error("No arg for -skip");
      } else
        error("invalid arg for create-repo %s", args[i]);
      i++;
    }

    Jar output = new Jar(project.getName());
    output.setDoNotTouchManifest();
    addClose(output);
    getInfo(project);
    if (isOk()) {
      for (Container c : project.getBuildpath()) {
        addContainer(skip, output, c);
      }
      for (Container c : project.getRunbundles()) {
        addContainer(skip, output, c);
      }
      if (isOk()) {
        try {
          output.write(out);
        } catch (Exception e) {
          error("Could not write mini repo %s", e);
        }
      }
    }
  }

  private void addContainer(Collection<Instruction> skip, Jar output, Container c)
      throws Exception {
    trace("add container " + c);
    String prefix = "cnf/repo/";

    switch (c.getType()) {
    case ERROR:
      error("Dependencies include %s", c.getError());
      return;

    case REPO:
    case EXTERNAL: {
      String name = getName(skip, prefix, c, ".jar");
      if (name != null)
        output.putResource(name, new FileResource(c.getFile()));
      trace("storing %s in %s", c, name);
      break;
    }

    case PROJECT:
      trace("not storing project " + c);
      break;

    case LIBRARY: {
      String name = getName(skip, prefix, c, ".lib");
      if (name != null) {
        output.putResource(name, new FileResource(c.getFile()));
        trace("store library %s", name);
        for (Container child : c.getMembers()) {
          trace("store member %s", child);
          addContainer(skip, output, child);
        }
      }
    }
    }
  }

  String getName(Collection<Instruction> skip, String prefix, Container c, String extension)
      throws Exception {
    Manifest m = c.getManifest();
    try {
      if (m == null) {
        error("No manifest found for %s", c);
        return null;
      }
      String bsn = m.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME);
      if (bsn == null) {
        error("No bsn in manifest: %s", c);
        return null;
      }
      for (Instruction instr : skip) {
        if (instr.matches(bsn)) {
          if (instr.isNegated()) // - * - = +!
            break;
          else
            return null; // skip it
        }
      }

      int n = bsn.indexOf(';');
      if (n > 0) {
        bsn = bsn.substring(0, n).trim();
      }
      String version = m.getMainAttributes().getValue(BUNDLE_VERSION);
      if (version == null)
        version = "0";

      Version v = new Version(version);
      version = v.getMajor() + "." + v.getMinor() + "." + v.getMicro();
      if (c.getFile() != null && c.getFile().getName().endsWith("-latest.jar"))
        version = "latest";
      return prefix + bsn + "/" + bsn + "-" + version + extension;

    } catch (Exception e) {
      error("Could not store repo file %s", c);
    }
    return null;
  }

  private void deliverables(String[] args, int i) throws Exception {
    Project project = getProject();
    long start = System.currentTimeMillis();
    Collection<Project> projects = project.getWorkspace().getAllProjects();
    List<Container> containers = new ArrayList<Container>();
    for (Project p : projects) {
      containers.addAll(p.getDeliverables());
    }
    long duration = System.currentTimeMillis() - start;
    System.out.println("Took " + duration + " ms");

    for (Container c : containers) {
      Version v = new Version(c.getVersion());
      System.out.printf("%-40s %d.%d.%d %s\n", c.getBundleSymbolicName(), v.getMajor(),
          v.getMinor(), v.getMicro(), c.getFile());
    }

  }

  private int doMacro(String[] args, int i) throws Exception {
    String result;
    for (; i < args.length; i++) {
      String cmd = args[i];
      cmd = cmd.replaceAll("@\\{([^}])\\}", "\\${$1}");
      cmd = cmd.replaceAll(":", ";");
      cmd = cmd.replaceAll("[^$](.*)", "\\${$0}");
      result = getReplacer().process(cmd);
      if (result != null && result.length() != 0) {
        Collection<String> parts = split(result);
        for (String s : parts) {
          out.println(s);
        }
      } else
        out.println("No result for " + cmd);

    }
    return i;
  }

  private void doRelease(String[] args, int i) throws Exception {
    Project project = getProject();
    project.release(false);
    getInfo(project);
  }

  /**
   * Cross reference every class in the jar file to the files it references
   *
   * @param args
   * @param i
   */

  private void doXref(String[] args, int i) {
    for (; i < args.length; i++) {
      try {
        File file = new File(args[i]);
        Jar jar = new Jar(file.getName(), file);
        try {
          for (Map.Entry<String, Resource> entry : jar.getResources().entrySet()) {
            String key = entry.getKey();
            Resource r = entry.getValue();
            if (key.endsWith(".class")) {
              InputStream in = r.openInputStream();
              Clazz clazz = new Clazz(key, r);
              out.print(key);
              Set<String> xref = clazz.parseClassFile();
              in.close();
              for (String element : xref) {
                out.print("\t");
                out.print(element);
              }
              out.println();
            }
          }
        } finally {
          jar.close();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

  }

  private void doEclipse(String[] args, int i) throws Exception {
    File dir = new File("").getAbsoluteFile();
    if (args.length == i)
      doEclipse(dir);
    else {
      for (; i < args.length; i++) {
        doEclipse(new File(dir, args[i]));
      }
    }
  }

  private void doEclipse(File file) throws Exception {
    if (!file.isDirectory())
      error("Eclipse requires a path to a directory: " + file.getAbsolutePath());
    else {
      File cp = new File(file, ".classpath");
      if (!cp.exists()) {
        error("Cannot find .classpath in project directory: " + file.getAbsolutePath());
      } else {
        EclipseClasspath eclipse = new EclipseClasspath(this, file.getParentFile(), file);
        out.println("Classpath    " + eclipse.getClasspath());
        out.println("Dependents   " + eclipse.getDependents());
        out.println("Sourcepath   " + eclipse.getSourcepath());
        out.println("Output       " + eclipse.getOutput());
        out.println();
      }
    }

  }

  final static int  BUILD_SOURCES  = 1;
  final static int  BUILD_POM    = 2;
  final static int  BUILD_FORCE    = 4;

  private void doBuild(String[] args, int i) throws Exception {
    File classpath[] = new File[0];
    File workspace = null;
    File sourcepath[] = new File[0];
    File output = null;
    String eclipse = "";
    int options = 0;

    for (; i < args.length; i++) {
      if ("-workspace".startsWith(args[i])) {
        workspace = new File(args[++i]);
      } else if ("-classpath".startsWith(args[i])) {
        classpath = getClasspath(args[++i]);
      } else if ("-sourcepath".startsWith(args[i])) {
        String arg = args[++i];
        String spaces[] = arg.split("\\s*,\\s*");
        sourcepath = new File[spaces.length];
        for (int j = 0; j < spaces.length; j++) {
          File f = new File(spaces[j]);
          if (!f.exists())
            error("No such sourcepath entry: " + f.getAbsolutePath());
          sourcepath[j] = f;
        }
      } else if ("-eclipse".startsWith(args[i])) {
        eclipse = args[++i];
      } else if ("-noeclipse".startsWith(args[i])) {
        eclipse = null;
      } else if ("-output".startsWith(args[i])) {
        output = new File(args[++i]);
      } else if ("-sources".startsWith(args[i])) {
        options |= BUILD_SOURCES;
      } else if ("-pom".startsWith(args[i])) {
        options |= BUILD_POM;
      } else if ("-force".startsWith(args[i])) {
        options |= BUILD_FORCE;
      } else {
        if (args[i].startsWith("-"))
          error("Invalid option for bnd: " + args[i]);
        else {
          File properties = new File(args[i]);

          if (!properties.exists())
            error("Cannot find bnd file: " + args[i]);
          else {
            if (workspace == null)
              workspace = properties.getParentFile();

            doBuild(properties, classpath, sourcepath, output, eclipse, workspace,
                options, new HashSet<File>());
          }
          output = null;
        }
      }
    }

  }

  private File[] getClasspath(String string) {
    String spaces[] = string.split("\\s*,\\s*");
    File classpath[] = new File[spaces.length];
    for (int j = 0; j < spaces.length; j++) {
      File f = new File(spaces[j]);
      if (!f.exists())
        error("No such classpath entry: " + f.getAbsolutePath());
      classpath[j] = f;
    }
    return classpath;
  }

  private void doBuild(File properties, File classpath[], File sourcepath[], File output,
      String eclipse, File workspace, int options, Set<File> building) throws Exception {

    properties = properties.getAbsoluteFile();
    if (building.contains(properties)) {
      error("Circular dependency in pre build " + properties);
      return;
    }
    building.add(properties);

    Builder builder = new Builder();
    try {
      builder.setPedantic(isPedantic());
      builder.setProperties(properties);

      if (output == null) {
        String out = builder.getProperty("-output");
        if (out != null) {
          output = getFile(properties.getParentFile(), out);
          if (!output.getName().endsWith(".jar"))
            output.mkdirs();
        } else
          output = properties.getAbsoluteFile().getParentFile();
      }

      String prebuild = builder.getProperty("-prebuild");
      if (prebuild != null)
        prebuild(prebuild, properties.getParentFile(), classpath, sourcepath, output,
            eclipse, workspace, options, building);

      doEclipse(builder, properties, classpath, sourcepath, eclipse, workspace);

      if ((options & BUILD_SOURCES) != 0)
        builder.getProperties().setProperty("-sources", "true");

      if (failok)
        builder.setProperty(Analyzer.FAIL_OK, "true");
      Jar jar = builder.build();
      getInfo(builder);
      if (getErrors().size() > 0 && !failok)
        return;

      String name = builder.getBsn() + DEFAULT_JAR_EXTENSION;

      if (output.isDirectory())
        output = new File(output, name);

      output.getParentFile().mkdirs();

      if ((options & BUILD_POM) != 0) {
        Resource r = new PomFromManifest(jar.getManifest());
        jar.putResource("pom.xml", r);
        String path = output.getName().replaceAll("\\.jar$", ".pom");
        if (path.equals(output.getName()))
          path = output.getName() + ".pom";
        File pom = new File(output.getParentFile(), path);
        OutputStream out = new FileOutputStream(pom);
        try {
          r.write(out);
        } finally {
          out.close();
        }
      }
      jar.setName(output.getName());

      String msg = "";
      if (!output.exists() || output.lastModified() <= jar.lastModified()
          || (options & BUILD_FORCE) != 0) {
        jar.write(output);
      } else {
        msg = "(not modified)";
      }
      statistics(jar, output, msg);
    } finally {
      builder.close();
    }
  }

  private void prebuild(String prebuild, File base, File[] classpath, File[] sourcepath,
      File output, String eclipse2, File workspace, int options, Set<File> building)
      throws Exception {

    // Force the output a directory
    if (output.isFile())
      output = output.getParentFile();

    Collection<String> parts = Processor.split(prebuild);
    for (String part : parts) {
      File f = new File(part);
      if (!f.exists())
        f = new File(base, part);
      if (!f.exists()) {
        error("Trying to build a non-existent file: " + parts);
        continue;
      }
      try {
        doBuild(f, classpath, sourcepath, output, eclipse2, workspace, options, building);
      } catch (Exception e) {
        error("Trying to build: " + part + " " + e);
      }
    }
  }

  private void statistics(Jar jar, File output, String msg) {
    out.println(jar.getName() + " " + jar.getResources().size() + " " + output.length() + msg);
  }

  /**
   * @param properties
   * @param classpath
   * @param eclipse
   * @return
   * @throws IOException
   */
  void doEclipse(Builder builder, File properties, File[] classpath, File sourcepath[],
      String eclipse, File workspace) throws IOException {
    if (eclipse != null) {
      File project = new File(workspace, eclipse).getAbsoluteFile();
      if (project.exists() && project.isDirectory()) {
        try {

          EclipseClasspath path = new EclipseClasspath(this, project.getParentFile(),
              project);
          List<File> newClasspath = Create.copy(Arrays.asList(classpath));
          newClasspath.addAll(path.getClasspath());
          classpath = (File[]) newClasspath.toArray(classpath);

          List<File> newSourcepath = Create.copy(Arrays.asList(sourcepath));
          newSourcepath.addAll(path.getSourcepath());
          sourcepath = (File[]) newSourcepath.toArray(sourcepath);
        } catch (Exception e) {
          if (eclipse.length() > 0)
            error("Eclipse specified (" + eclipse + ") but getting error processing: "
                + e);
        }
      } else {
        if (eclipse.length() > 0)
          error("Eclipse specified (" + eclipse + ") but no project directory found");
      }
    }
    builder.setClasspath(classpath);
    builder.setSourcepath(sourcepath);
  }

  private void doHelp() {
    doHelp(new String[0], 0);
  }

  private void doHelp(String[] args, int i) {
    if (args.length <= i) {
      out.println("bnd -failok? -exceptions? ( wrap | print | build | eclipse | xref | view )?");
      out.println("See http://www.aQute.biz/Code/Bnd");
    } else {
      while (args.length > i) {
        if ("wrap".equals(args[i])) {
          out.println("bnd wrap (-output <file|dir>)? (-properties <file>)? <jar-file>");
        } else if ("print".equals(args[i])) {
          out.println("bnd wrap -verify? -manifest? -list? -eclipse <jar-file>");
        } else if ("build".equals(args[i])) {
          out.println("bnd build (-output <file|dir>)? (-classpath <list>)? (-sourcepath <list>)? ");
          out.println("    -eclipse? -noeclipse? -sources? <bnd-file>");
        } else if ("eclipse".equals(args[i])) {
          out.println("bnd eclipse");
        } else if ("view".equals(args[i])) {
          out.println("bnd view <file> <resource-names>+");
        }
        i++;
      }
    }
  }

  final static int  VERIFY    = 1;

  final static int  MANIFEST  = 2;

  final static int  LIST    = 4;

  final static int  ECLIPSE    = 8;
  final static int  IMPEXP    = 16;
  final static int  USES    = 32;
  final static int  USEDBY    = 64;
  final static int  COMPONENT  = 128;
  final static int  METATYPE  = 256;

  static final int  HEX      = 0;

  private void doPrint(String[] args, int i) throws Exception {
    int options = 0;

    for (; i < args.length; i++) {
      if ("-verify".startsWith(args[i]))
        options |= VERIFY;
      else if ("-manifest".startsWith(args[i]))
        options |= MANIFEST;
      else if ("-list".startsWith(args[i]))
        options |= LIST;
      else if ("-eclipse".startsWith(args[i]))
        options |= ECLIPSE;
      else if ("-impexp".startsWith(args[i]))
        options |= IMPEXP;
      else if ("-uses".startsWith(args[i]))
        options |= USES;
      else if ("-usedby".startsWith(args[i]))
        options |= USEDBY;
      else if ("-component".startsWith(args[i]))
        options |= COMPONENT;
      else if ("-metatype".startsWith(args[i]))
        options |= METATYPE;
      else if ("-all".startsWith(args[i]))
        options = -1;
      else {
        if (args[i].startsWith("-"))
          error("Invalid option for print: " + args[i]);
        else
          doPrint(args[i], options);
      }
    }
  }

  public void doPrint(String string, int options) throws Exception {
    File file = new File(string);
    if (!file.exists())
      error("File to print not found: " + string);
    else {
      if (options == 0)
        options = VERIFY | MANIFEST | IMPEXP | USES;
      doPrint(file, options);
    }
  }

  private void doPrint(File file, int options) throws ZipException, IOException, Exception {

    Jar jar = new Jar(file.getName(), file);
    try {
      if ((options & VERIFY) != 0) {
        Verifier verifier = new Verifier(jar);
        verifier.setPedantic(isPedantic());
        verifier.verify();
        getInfo(verifier);
      }
      if ((options & MANIFEST) != 0) {
        Manifest manifest = jar.getManifest();
        if (manifest == null)
          warning("JAR has no manifest " + file);
        else {
          out.println("[MANIFEST " + jar.getName() + "]");
          SortedSet<String> sorted = new TreeSet<String>();
          for (Object element : manifest.getMainAttributes().keySet()) {
            sorted.add(element.toString());
          }
          for (String key : sorted) {
            Object value = manifest.getMainAttributes().getValue(key);
            format("%-40s %-40s\r\n", new Object[] { key, value });
          }
        }
        out.println();
      }
      if ((options & IMPEXP) != 0) {
        out.println("[IMPEXP]");
        Manifest m = jar.getManifest();
        if (m != null) {
          Map<String, Map<String, String>> imports = parseHeader(m.getMainAttributes()
              .getValue(Analyzer.IMPORT_PACKAGE));
          Map<String, Map<String, String>> exports = parseHeader(m.getMainAttributes()
              .getValue(Analyzer.EXPORT_PACKAGE));
          for (String p : exports.keySet()) {
            if (imports.containsKey(p)) {
              Map<String, String> attrs = imports.get(p);
              if (attrs.containsKey(Constants.VERSION_ATTRIBUTE)) {
                exports.get(p).put("imported-as", attrs.get(VERSION_ATTRIBUTE));
              }
            }
          }
          print("Import-Package", new TreeMap<String, Map<String, String>>(imports));
          print("Export-Package", new TreeMap<String, Map<String, String>>(exports));
        } else
          warning("File has no manifest");
      }

      if ((options & (USES | USEDBY)) != 0) {
        out.println();
        Analyzer analyzer = new Analyzer();
        analyzer.setPedantic(isPedantic());
        analyzer.setJar(jar);
        analyzer.analyze();
        if ((options & USES) != 0) {
          out.println("[USES]");
          printMapOfSets(new TreeMap<String, Set<String>>(analyzer.getUses()));
          out.println();
        }
        if ((options & USEDBY) != 0) {
          out.println("[USEDBY]");
          printMapOfSets(invertMapOfCollection(analyzer.getUses()));
        }
      }

      if ((options & COMPONENT) != 0) {
        printComponents(out, jar);
      }

      if ((options & METATYPE) != 0) {
        printMetatype(out, jar);
      }

      if ((options & LIST) != 0) {
        out.println("[LIST]");
        for (Map.Entry<String, Map<String, Resource>> entry : jar.getDirectories()
            .entrySet()) {
          String name = entry.getKey();
          Map<String, Resource> contents = entry.getValue();
          out.println(name);
          if (contents != null) {
            for (String element : contents.keySet()) {
              int n = element.lastIndexOf('/');
              if (n > 0)
                element = element.substring(n + 1);
              out.print("  ");
              out.print(element);
              String path = element;
              if (name.length() != 0)
                path = name + "/" + element;
              Resource r = contents.get(path);
              if (r != null) {
                String extra = r.getExtra();
                if (extra != null) {

                  out.print(" extra='" + escapeUnicode(extra) + "'");
                }
              }
              out.println();
            }
          } else {
            out.println(name + " <no contents>");
          }
        }
        out.println();
      }
    } finally {
      jar.close();
    }
  }

  private final char nibble(int i) {
    return "0123456789ABCDEF".charAt(i & 0xF);
  }

  private final String escapeUnicode(String s) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c >= ' ' && c <= '~' && c != '\\')
        sb.append(c);
      else {
        sb.append("\\u");
        sb.append(nibble(c >> 12));
        sb.append(nibble(c >> 8));
        sb.append(nibble(c >> 4));
        sb.append(nibble(c));
      }
    }
    return sb.toString();
  }

  /**
   * Print the components in this JAR.
   *
   * @param jar
   */
  private void printComponents(PrintStream out, Jar jar) throws Exception {
    out.println("[COMPONENTS]");
    Manifest manifest = jar.getManifest();
    if (manifest == null) {
      out.println("No manifest");
      return;
    }

    String componentHeader = manifest.getMainAttributes().getValue(Constants.SERVICE_COMPONENT);
    Map<String, Map<String, String>> clauses = parseHeader(componentHeader);
    for (String path : clauses.keySet()) {
      out.println(path);

      Resource r = jar.getResource(path);
      if (r != null) {
        InputStreamReader ir = new InputStreamReader(r.openInputStream(),
            Constants.DEFAULT_CHARSET);
        OutputStreamWriter or = new OutputStreamWriter(out, Constants.DEFAULT_CHARSET);
        try {
          copy(ir, or);
        } finally {
          or.flush();
          ir.close();
        }
      } else {
        out.println("  - no resource");
        warning("No Resource found for service component: " + path);
      }
    }
    out.println();
  }

  /**
   * Print the metatypes in this JAR.
   *
   * @param jar
   */
  private void printMetatype(PrintStream out, Jar jar) throws Exception {
    out.println("[METATYPE]");
    Manifest manifest = jar.getManifest();
    if (manifest == null) {
      out.println("No manifest");
      return;
    }

    Map<String, Resource> map = jar.getDirectories().get("OSGI-INF/metatype");
    if (map != null) {
      for (Map.Entry<String, Resource> entry : map.entrySet()) {
        out.println(entry.getKey());
        IO.copy(entry.getValue().openInputStream(), out);
        out.println();
      }
      out.println();
    }
  }

  Map<String, Set<String>> invertMapOfCollection(Map<String, Set<String>> map) {
    Map<String, Set<String>> result = new TreeMap<String, Set<String>>();
    for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
      String name = entry.getKey();
      if (name.startsWith("java.") && !name.equals("java.sql"))
        continue;

      Collection<String> used = entry.getValue();
      for (String n : used) {
        if (n.startsWith("java.") && !n.equals("java.sql"))
          continue;
        Set<String> set = result.get(n);
        if (set == null) {
          set = new TreeSet<String>();
          result.put(n, set);
        }
        set.add(name);
      }
    }
    return result;
  }

  void printMapOfSets(Map<String, Set<String>> map) {
    for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
      String name = entry.getKey();
      Set<String> used = new TreeSet<String>(entry.getValue());

      for (Iterator<String> k = used.iterator(); k.hasNext();) {
        String n = (String) k.next();
        if (n.startsWith("java.") && !n.equals("java.sql"))
          k.remove();
      }
      String list = vertical(40, used);
      format("%-40s %s", new Object[] { name, list });
    }
  }

  String vertical(int padding, Set<String> used) {
    StringBuffer sb = new StringBuffer();
    String del = "";
    for (Iterator<String> u = used.iterator(); u.hasNext();) {
      String name = (String) u.next();
      sb.append(del);
      sb.append(name);
      sb.append("\r\n");
      del = pad(padding);
    }
    if (sb.length() == 0)
      sb.append("\r\n");
    return sb.toString();
  }

  String pad(int i) {
    StringBuffer sb = new StringBuffer();
    while (i-- > 0)
      sb.append(' ');
    return sb.toString();
  }

  /**
   * View files from JARs
   *
   * We parse the commandline and print each file on it.
   *
   * @param args
   * @param i
   * @throws Exception
   */
  private void doView(String[] args, int i) throws Exception {
    int options = 0;
    String charset = "UTF-8";
    File output = null;

    for (; i < args.length; i++) {
      if ("-charset".startsWith(args[i]))
        charset = args[++i];
      else if ("-output".startsWith(args[i])) {
        output = new File(args[++i]);
      } else
        break;
    }

    if (i >= args.length) {
      error("Insufficient arguments for view, no JAR");
      return;
    }
    String jar = args[i++];
    if (i >= args.length) {
      error("No Files to view");
      return;
    }

    doView(jar, args, i, charset, options, output);
  }

  private void doView(String jar, String[] args, int i, String charset, int options, File output) {
    File path = new File(jar).getAbsoluteFile();
    File dir = path.getParentFile();
    if (dir == null) {
      dir = new File("");
    }
    if (!dir.exists()) {
      error("No such file: " + dir.getAbsolutePath());
      return;
    }

    String name = path.getName();
    if (name == null)
      name = "META-INF/MANIFEST.MF";

    Instruction instruction = Instruction.getPattern(path.getName());

    File[] children = dir.listFiles();
    for (int j = 0; j < children.length; j++) {
      String base = children[j].getName();
      // out.println("Considering: " +
      // children[j].getAbsolutePath() + " " +
      // instruction.getPattern());
      if (instruction.matches(base) ^ instruction.isNegated()) {
        for (; i < args.length; i++) {
          doView(children[j], args[i], charset, options, output);
        }
      }
    }
  }

  private void doView(File file, String resource, String charset, int options, File output) {
    // out.println("doView:" + file.getAbsolutePath() );
    try {
      Instruction instruction = Instruction.getPattern(resource);
      FileInputStream fin = new FileInputStream(file);
      ZipInputStream in = new ZipInputStream(fin);
      ZipEntry entry = in.getNextEntry();
      while (entry != null) {
        // out.println("view " + file + ": "
        // + instruction.getPattern() + ": " + entry.getName()
        // + ": " + output + ": "
        // + instruction.matches(entry.getName()));
        if (instruction.matches(entry.getName()) ^ instruction.isNegated())
          doView(entry.getName(), in, charset, options, output);
        in.closeEntry();
        entry = in.getNextEntry();
      }
      in.close();
      fin.close();
    } catch (Exception e) {
      out.println("Can not process: " + file.getAbsolutePath());
      e.printStackTrace();
    }
  }

  private void doView(String name, ZipInputStream in, String charset, int options, File output)
      throws Exception {
    int n = name.lastIndexOf('/');
    name = name.substring(n + 1);

    InputStreamReader rds = new InputStreamReader(in, charset);
    OutputStreamWriter wrt = new OutputStreamWriter(out, Constants.DEFAULT_CHARSET);
    if (output != null)
      if (output.isDirectory())
        wrt = new FileWriter(new File(output, name));
      else
        wrt = new FileWriter(output);

    copy(rds, wrt);
    // rds.close(); also closes the stream which closes our zip it
    // seems
    if (output != null)
      wrt.close();
    else
      wrt.flush();
  }

  private void copy(Reader rds, Writer wrt) throws IOException {
    char buffer[] = new char[1024];
    int size = rds.read(buffer);
    while (size > 0) {
      wrt.write(buffer, 0, size);
      size = rds.read(buffer);
    }
  }

  private void print(String msg, Map<String, Map<String, String>> ports) {
    if (ports.isEmpty())
      return;
    out.println(msg);
    for (Map.Entry<String, Map<String, String>> entry : ports.entrySet()) {
      String key = entry.getKey();
      Map<String, String> clause = Create.copy(entry.getValue());
      clause.remove("uses:");
      format("  %-38s %s\r\n", key.trim(), clause.isEmpty() ? "" : clause.toString());
    }
  }

  private void format(String string, Object... objects) {
    if (objects == null || objects.length == 0)
      return;

    StringBuffer sb = new StringBuffer();
    int index = 0;
    for (int i = 0; i < string.length(); i++) {
      char c = string.charAt(i);
      switch (c) {
      case '%':
        String s = objects[index++] + "";
        int width = 0;
        int justify = -1;

        i++;

        c = string.charAt(i++);
        switch (c) {
        case '-':
          justify = -1;
          break;
        case '+':
          justify = 1;
          break;
        case '|':
          justify = 0;
          break;
        default:
          --i;
        }
        c = string.charAt(i++);
        while (c >= '0' && c <= '9') {
          width *= 10;
          width += c - '0';
          c = string.charAt(i++);
        }
        if (c != 's') {
          throw new IllegalArgumentException("Invalid sprintf format:  " + string);
        }

        if (s.length() > width)
          sb.append(s);
        else {
          switch (justify) {
          case -1:
            sb.append(s);
            for (int j = 0; j < width - s.length(); j++)
              sb.append(" ");
            break;

          case 1:
            for (int j = 0; j < width - s.length(); j++)
              sb.append(" ");
            sb.append(s);
            break;

          case 0:
            int spaces = (width - s.length()) / 2;
            for (int j = 0; j < spaces; j++)
              sb.append(" ");
            sb.append(s);
            for (int j = 0; j < width - s.length() - spaces; j++)
              sb.append(" ");
            break;
          }
        }
        break;

      default:
        sb.append(c);
      }
    }
    out.print(sb);
  }

  private void doWrap(String[] args, int i) throws Exception {
    int options = 0;
    File properties = null;
    File output = null;
    File classpath[] = null;
    for (; i < args.length; i++) {
      if ("-output".startsWith(args[i]))
        output = new File(args[++i]);
      else if ("-properties".startsWith(args[i]))
        properties = new File(args[++i]);
      else if ("-classpath".startsWith(args[i])) {
        classpath = getClasspath(args[++i]);
      } else {
        File bundle = new File(args[i]);
        doWrap(properties, bundle, output, classpath, options, null);
      }
    }
  }

  public boolean doWrap(File properties, File bundle, File output, File classpath[], int options,
      Map<String, String> additional) throws Exception {
    if (!bundle.exists()) {
      error("No such file: " + bundle.getAbsolutePath());
      return false;
    } else {
      Analyzer analyzer = new Analyzer();
      try {
        analyzer.setPedantic(isPedantic());
        analyzer.setJar(bundle);
        Jar dot = analyzer.getJar();

        if (properties != null) {
          analyzer.setProperties(properties);
        }
        if (additional != null)
          analyzer.putAll(additional, false);

        if (analyzer.getProperty(Analyzer.IMPORT_PACKAGE) == null)
          analyzer.setProperty(Analyzer.IMPORT_PACKAGE, "*;resolution:=optional");

        if (analyzer.getProperty(Analyzer.BUNDLE_SYMBOLICNAME) == null) {
          Pattern p = Pattern.compile("(" + Verifier.SYMBOLICNAME.pattern()
              + ")(-[0-9])?.*\\.jar");
          String base = bundle.getName();
          Matcher m = p.matcher(base);
          base = "Untitled";
          if (m.matches()) {
            base = m.group(1);
          } else {
            error("Can not calculate name of output bundle, rename jar or use -properties");
          }
          analyzer.setProperty(Analyzer.BUNDLE_SYMBOLICNAME, base);
        }

        if (analyzer.getProperty(Analyzer.EXPORT_PACKAGE) == null) {
          String export = analyzer.calculateExportsFromContents(dot);
          analyzer.setProperty(Analyzer.EXPORT_PACKAGE, export);
        }

        if (classpath != null)
          analyzer.setClasspath(classpath);

        analyzer.mergeManifest(dot.getManifest());

        //
        // Cleanup the version ..
        //
        String version = analyzer.getProperty(Analyzer.BUNDLE_VERSION);
        if (version != null) {
          version = Builder.cleanupVersion(version);
          analyzer.setProperty(Analyzer.BUNDLE_VERSION, version);
        }

        if (output == null)
          if (properties != null)
            output = properties.getAbsoluteFile().getParentFile();
          else
            output = bundle.getAbsoluteFile().getParentFile();

        String path = bundle.getName();
        if (path.endsWith(DEFAULT_JAR_EXTENSION))
          path = path.substring(0, path.length() - DEFAULT_JAR_EXTENSION.length())
              + DEFAULT_BAR_EXTENSION;
        else
          path = bundle.getName() + DEFAULT_BAR_EXTENSION;

        if (output.isDirectory())
          output = new File(output, path);

        analyzer.calcManifest();
        Jar jar = analyzer.getJar();
        getInfo(analyzer);
        statistics(jar, output, "");
        File f = File.createTempFile("tmpbnd", ".jar");
        f.deleteOnExit();
        try {
          jar.write(f);
          jar.close();
          if (!f.renameTo(output)) {
            IO.copy(f, output);
          }
        } finally {
          f.delete();
        }
        return getErrors().size() == 0;
      } finally {
        analyzer.close();
      }
    }
  }

  void doDiff(String args[], int first) throws Exception {
    File base = new File("");
    boolean strict = false;
    Jar targets[] = new Jar[2];
    int n = 0;

    for (int i = first; i < args.length; i++) {
      if ("-d".equals(args[i]))
        base = getFile(base, args[++i]);
      else if ("-strict".equals(args[i]))
        strict = "true".equalsIgnoreCase(args[++i]);
      else if (args[i].startsWith("-"))
        error("Unknown option for diff: " + args[i]);
      else {
        if (n >= 2)
          System.err.println("Must have 2 files ... not more");
        else {
          File f = getFile(base, args[i]);
          if (!f.isFile())
            System.err.println("Not a file: " + f);
          else {
            try {
              Jar jar = new Jar(f);
              targets[n++] = jar;
            } catch (Exception e) {
              System.err.println("Not a JAR: " + f);
            }
          }
        }
      }
    }
    if (n != 2) {
      System.err.println("Must have 2 files ...");
      return;
    }
    Diff diff = new Diff();

    Map<String, Object> map = diff.diff(targets[0], targets[1], strict);
    diff.print(System.out, map, 0);

    for (Jar jar : targets) {
      jar.close();
    }
    diff.close();
  }

  public void setOut(PrintStream out) {
    this.out = out;
  }

  public Project getProject() throws Exception {
    if (project != null)
      return project;

    try {
      project = Workspace.getProject(getBase());
      if (project == null)
        return null;

      if (!project.isValid())
        return null;

      return project;
    } catch (IllegalArgumentException e) {
      return null;
    }
  }

  /**
   * Printout all the variables.
   *
   * @param args
   * @param i
   * @throws Exception
   */
  public void debug(String args[], int i) throws Exception {
    Project project = getProject();
    System.out.println("Project: " + project);
    Properties p = project.getFlattenedProperties();
    for (Object k : p.keySet()) {
      String key = (String) k;
      String s = p.getProperty(key);
      Collection<String> l = null;

      if (s.indexOf(',') > 0)
        l = split(s);
      else if (s.indexOf(':') > 0)
        l = split(s, "\\s*:\\s*");
      if (l != null) {
        String del = key;
        for (String ss : l) {
          System.out.printf("%-40s %s\n", del, ss);
          del = "";
        }
      } else
        System.out.printf("%-40s %s\n", key, s);
    }
  }

  /**
   * Manage the repo.
   *
   * <pre>
   *  repo
   *      list
   *      put &lt;file|url&gt;
   *      get &lt;bsn&gt; (&lt;version&gt;)?
   *      fetch &lt;file|url&gt;
   * </pre>
   */

  public void repo(String args[], int i) throws Exception {
    String bsn = null;
    String version = null;
    List<RepositoryPlugin> repos = new ArrayList<RepositoryPlugin>();
    RepositoryPlugin writable = null;

    Project p = Workspace.getProject(getBase());
    if (p != null) {
      repos.addAll(p.getWorkspace().getRepositories());
      for (Iterator<RepositoryPlugin> rp = repos.iterator(); rp.hasNext();) {
        RepositoryPlugin rpp = rp.next();
        if (rpp.canWrite()) {
          writable = rpp;
          break;
        }
      }
    }

    for (; i < args.length; i++) {
      if ("repos".equals(args[i])) {
        int n = 0;
        for (RepositoryPlugin repo : repos) {
          out.printf("%3d. %s\n", n++, repo);
        }
        return;
      } else if ("list".equals(args[i])) {
        String mask = null;
        if (i < args.length - 1) {
          mask = args[++i];
        }
        repoList(repos, mask);
        return;
      } else if ("--repo".equals(args[i]) || "-r".equals(args[i])) {
        String location = args[++i];
        if (location.equals("maven")) {
          System.out.println("Maven");
          MavenRemoteRepository maven = new MavenRemoteRepository();
          maven.setProperties(new HashMap<String, String>());
          maven.setReporter(this);
          repos = Arrays.asList((RepositoryPlugin) maven);
        } else {
          FileRepo repo = new FileRepo();
          repo.setReporter(this);
          repo.setLocation(location);
          repos = Arrays.asList((RepositoryPlugin) repo);
          writable = repo;
        }
      } else if ("spring".equals(args[i])) {
//        if (bsn == null || version == null) {
//          error("--bsn and --version must be set before spring command is used");
//        } else {
//          String url = String
//              .format("http://www.springsource.com/repository/app/bundle/version/download?name=%s&version=%s&type=binary",
//                  bsn, version);
//          repoPut(writable, p, url, bsn, version);
//        }
        error("not supported anymore");
        return;
      } else if ("put".equals(args[i])) {
        while ( i <args.length-1) {
          String source = args[++i];
          try {

            URL url = IO.toURL(source, getBase());
            trace("put from %s", url);
            InputStream in = url.openStream();
            try {
              Jar jar = new Jar(url.toString(), in);
              Verifier verifier = new Verifier(jar);
              verifier.verify();
              getInfo(verifier);
              if ( isOk()) {
                File put = writable.put(jar);
                trace("stored in %s", put);
              }
            } finally {
              in.close();
            }
          } catch( Exception e) {
            error("putting %s into %s, exception: %s", source, writable,e);
          }
        }
        return;
      } else if ("get".equals(args[i])) {
        if ( i < args.length) {
          error("repo get requires a bsn, see repo help");
          return;
        }
        bsn = args[i++];
        if ( i < args.length) {
          error("repo get requires a version, see repo help");
          return;
        }
        version = args[i++];

        for (RepositoryPlugin repo : repos) {
          File f = repo.get(bsn, version, Strategy.LOWEST, null);
          if (f != null) {
            if (i < args.length) {
              File out = getFile(args[i++]);
              IO.copy(f, out);
            } else
              out.println(f);

            return;
          }
        }
        error("cannot find %s-%s in %s", bsn, version, repos);
        return;
      }
    }

    if (i < args.length && !"help".equals(args[i]))
      out.println("Unknown repo command: " + args[i]);

    out.println(" bnd repo [--repo|-r ('maven'| <dir>)]*");
    out.println("        repos                          # list the repositories");
    out.println("        list                           # list all content (not always possible)");
    out.println("        get <bsn> <version> <file>?    # get an artifact");
    out.println("        put <file>+                    # put in artifacts");
    out.println("        help");
    return;
  }
 
 
 

  private void repoPut(RepositoryPlugin writable, Project project, String file, String bsn,
      String version) throws Exception {
    Jar jar = null;
    int n = file.indexOf(':');
    if (n > 1 && n < 10) {
      jar = project.getValidJar(new URL(file));
    } else {
      File f = getFile(file);
      if (f.isFile()) {
        jar = project.getValidJar(f);
      }
    }

    if (jar != null) {
      Manifest manifest = jar.getManifest();
      if (bsn != null)
        manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, bsn);
      if (version != null)
        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, version);

      writable.put(jar);

    } else
      error("There is no such file or url: " + file);
  }

  void repoList(List<RepositoryPlugin> repos, String mask) throws Exception {
    trace("list repo " + repos + " " + mask);
    Set<String> bsns = new TreeSet<String>();
    for (RepositoryPlugin repo : repos) {
      bsns.addAll(repo.list(mask));
    }

    for (String bsn : bsns) {
      Set<Version> versions = new TreeSet<Version>();
      for (RepositoryPlugin repo : repos) {
        List<Version> result = repo.versions(bsn);
        if (result != null)
          versions.addAll(result);
      }
      out.printf("%-40s %s\n", bsn, versions);
    }
  }

  /**
   * Patch
   */

  void patch(String args[], int i) throws Exception {
    for (; i < args.length; i++) {
      if ("create".equals(args[i]) && i + 3 < args.length) {
        createPatch(args[++i], args[++i], args[++i]);
      } else if ("apply".equals(args[i]) && i + 3 < args.length) {
        applyPatch(args[++i], args[++i], args[++i]);
      } else if ("help".equals(args[i])) {
        out.println("patch (create <old> <new> <patch> | patch <old> <patch> <new>)");
      } else
        out.println("Patch does not recognize command? " + Arrays.toString(args));
    }
  }

  void createPatch(String old, String newer, String patch) throws Exception {
    Jar a = new Jar(new File(old));
    Manifest am = a.getManifest();
    Jar b = new Jar(new File(newer));
    Manifest bm = b.getManifest();

    Set<String> delete = newSet();

    for (String path : a.getResources().keySet()) {
      Resource br = b.getResource(path);
      if (br == null) {
        trace("DELETE    %s", path);
        delete.add(path);
      } else {
        Resource ar = a.getResource(path);
        if (isEqual(ar, br)) {
          trace("UNCHANGED %s", path);
          b.remove(path);
        } else
          trace("UPDATE    %s", path);
      }
    }

    bm.getMainAttributes().putValue("Patch-Delete", join(delete, ", "));
    bm.getMainAttributes().putValue("Patch-Version",
        am.getMainAttributes().getValue("Bundle-Version"));

    b.write(new File(patch));
    a.close();
    a.close();
  }

  private boolean isEqual(Resource ar, Resource br) throws Exception {
    InputStream ain = ar.openInputStream();
    try {
      InputStream bin = br.openInputStream();
      try {
        while (true) {
          int an = ain.read();
          int bn = bin.read();
          if (an == bn) {
            if (an == -1)
              return true;
          } else
            return false;
        }
      } finally {
        bin.close();
      }
    } finally {
      ain.close();
    }
  }

  void applyPatch(String old, String patch, String newer) throws Exception {
    Jar a = new Jar(new File(old));
    Jar b = new Jar(new File(patch));
    Manifest bm = b.getManifest();

    String patchDelete = bm.getMainAttributes().getValue("Patch-Delete");
    String patchVersion = bm.getMainAttributes().getValue("Patch-Version");
    if (patchVersion == null) {
      error("To patch, you must provide a patch bundle.\nThe given " + patch
          + " bundle does not contain the Patch-Version header");
      return;
    }

    Collection<String> delete = split(patchDelete);
    Set<String> paths = new HashSet<String>(a.getResources().keySet());
    paths.removeAll(delete);

    for (String path : paths) {
      Resource br = b.getResource(path);
      if (br == null)
        b.putResource(path, a.getResource(path));
    }

    bm.getMainAttributes().putValue("Bundle-Version", patchVersion);
    b.write(new File(newer));
    a.close();
    b.close();
  }

  /**
   * Run the tests from a prepared bnd file.
   *
   * @param args
   * @param i
   * @throws Exception
   */
  public void runtests(String args[], int i) throws Exception {
    int errors = 0;
    File cwd = new File("").getAbsoluteFile();
    Workspace ws = new Workspace(cwd);
    File reportDir = getFile("reports");

    IO.delete(reportDir);
    reportDir.mkdirs();

    Tag summary = new Tag("summary");
    summary.addAttribute("date", new Date());
    summary.addAttribute("ws", ws.getBase());

    try {
      boolean hadOne = false;

      for (; i < args.length; i++) {
        if (args[i].startsWith("-reportdir")) {
          reportDir = getFile(args[++i]).getAbsoluteFile();
          if (!reportDir.isDirectory())
            error("reportdir must be a directory " + reportDir);
        } else if (args[i].startsWith("-title")) {
          summary.addAttribute("title", args[++i]);
        } else if (args[i].startsWith("-dir")) {
          cwd = getFile(args[++i]).getAbsoluteFile();
        } else if (args[i].startsWith("-workspace")) {
          File tmp = getFile(args[++i]).getAbsoluteFile();
          ws = Workspace.getWorkspace(tmp);
        } else {
          File f = getFile(args[i]);
          errors += runtTest(f, ws, reportDir, summary);
          hadOne = true;
        }
      }

      if (!hadOne) {
        // See if we had any, if so, just use all files in
        // the current directory
        File[] files = cwd.listFiles();
        for (File f : files) {
          if (f.getName().endsWith(".bnd")) {
            errors += runtTest(f, ws, reportDir, summary);
          }
        }
      }
    } catch (Throwable e) {
      e.printStackTrace();
      error("FAILURE IN RUNTESTS", e);
      errors++;
    }
    if (errors > 0)
      summary.addAttribute("errors", errors);

    for (String error : getErrors()) {
      Tag e = new Tag("error");
      e.addContent(error);
    }

    for (String warning : getWarnings()) {
      Tag e = new Tag("warning");
      e.addContent(warning);
    }

    File r = getFile(reportDir + "/summary.xml");
    FileOutputStream out = new FileOutputStream(r);
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, Constants.DEFAULT_CHARSET));
    try {
      summary.print(0, pw);
    } finally {
      pw.close();
      out.close();
    }
    if (errors != 0)
      error("Errors found %s", errors);
  }

  private int runtTest(File testFile, Workspace ws, File reportDir, Tag summary) throws Exception {
    File tmpDir = new File(reportDir, "tmp");
    tmpDir.mkdirs();
    tmpDir.deleteOnExit();

    Tag test = new Tag(summary, "test");
    test.addAttribute("path", testFile.getAbsolutePath());
    if (!testFile.isFile()) {
      error("No bnd file: %s", testFile);
      test.addAttribute("exception", "No bnd file found");
      throw new FileNotFoundException("No bnd file found for " + testFile.getAbsolutePath());
    }

    Project project = new Project(ws, testFile.getAbsoluteFile().getParentFile(),
        testFile.getAbsoluteFile());
    project.setTrace(isTrace());
    project.setProperty(NOBUNDLES, "true");

    ProjectTester tester = project.getProjectTester();

    getInfo(project, project.toString() + ": ");

    if (!isOk())
      throw new IllegalStateException("Errors found while creating the bnd test project "
          + testFile.getAbsolutePath());

    tester.setContinuous(false);
    tester.setReportDir(tmpDir);
    test.addAttribute("title", project.toString());
    long start = System.currentTimeMillis();
    try {
      int errors = tester.test();

      Collection<File> reports = tester.getReports();
      for (File report : reports) {
        Tag bundle = new Tag(test, "bundle");
        File dest = new File(reportDir, report.getName());
        report.renameTo(dest);
        bundle.addAttribute("file", dest.getAbsolutePath());
        doPerReport(bundle, dest);
      }

      switch (errors) {
      case ProjectLauncher.OK:
        return 0;

      case ProjectLauncher.CANCELED:
        test.addAttribute("failed", "canceled");
        return 1;

      case ProjectLauncher.DUPLICATE_BUNDLE:
        test.addAttribute("failed", "duplicate bundle");
        return 1;

      case ProjectLauncher.ERROR:
        test.addAttribute("failed", "unknown reason");
        return 1;

      case ProjectLauncher.RESOLVE_ERROR:
        test.addAttribute("failed", "resolve error");
        return 1;

      case ProjectLauncher.TIMEDOUT:
        test.addAttribute("failed", "timed out");
        return 1;
      case ProjectLauncher.WARNING:
        test.addAttribute("warning", "true");
        return 1;

      case ProjectLauncher.ACTIVATOR_ERROR:
        test.addAttribute("failed", "activator error");
        return 1;

      default:
        if (errors > 0) {
          test.addAttribute("errors", errors);
          return errors;
        } else {
          test.addAttribute("failed", "unknown reason");
          return 1;
        }
      }
    } catch (Exception e) {
      test.addAttribute("failed", e);
      throw e;
    } finally {
      long duration = System.currentTimeMillis() - start;
      test.addAttribute("duration", (duration + 500) / 1000);
      getInfo(project, project.toString() + ": ");
    }
  }

  /**
   * Calculate the coverage if there is coverage info in the test file.
   */

  private void doPerReport(Tag report, File file) throws Exception {
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(true); // never forget this!
      DocumentBuilder builder = factory.newDocumentBuilder();
      Document doc = builder.parse(file);

      XPathFactory xpathFactory = XPathFactory.newInstance();
      XPath xpath = xpathFactory.newXPath();

      doCoverage(report, doc, xpath);
      doHtmlReport(report, file, doc, xpath);

    } catch (Exception e) {
      report.addAttribute("coverage-failed", e.getMessage());
    }
  }

  private void doCoverage(Tag report, Document doc, XPath xpath) throws XPathExpressionException {
    int bad = Integer.parseInt(xpath.evaluate("count(//method[count(ref)<2])", doc));
    int all = Integer.parseInt(xpath.evaluate("count(//method)", doc));
    report.addAttribute("coverage-bad", bad);
    report.addAttribute("coverage-all", all);
  }

  private void doHtmlReport(Tag report, File file, Document doc, XPath xpath) throws Exception {
    String path = file.getAbsolutePath();
    if (path.endsWith(".xml"))
      path = path.substring(0, path.length() - 4);
    path += ".html";
    File html = new File(path);
    trace("Creating html report: %s", html);

    TransformerFactory fact = TransformerFactory.newInstance();

    InputStream in = getClass().getResourceAsStream("testreport.xsl");
    if (in == null) {
      warning("Resource not found: test-report.xsl, no html report");
    } else {
      FileWriter out = new FileWriter(html);
      try {
        Transformer transformer = fact.newTransformer(new StreamSource(in));
        transformer.transform(new DOMSource(doc), new StreamResult(out));
        trace("Transformed");
      } finally {
        in.close();
        out.close();
      }
    }
  }

  /**
   * Extract a file from the JAR
   */

  public void doExtract(String args[], int i) throws Exception {
    if (i >= args.length) {
      error("No arguments for extract");
      return;
    }

    File f = getFile(args[i++]);
    if (!f.isFile()) {
      error("No JAR file to extract from: %s", f);
      return;
    }

    if (i == args.length) {
      System.out.println("FILES:");
      doPrint(f, LIST);
      return;
    }
    Jar j = new Jar(f);
    try {
      Writer output = new OutputStreamWriter(out, Constants.DEFAULT_CHARSET);
      while (i < args.length) {
        String path = args[i++];

        Resource r = j.getResource(path);
        if (r == null)
          error("No such resource: %s in %s", path, f);
        else {
          InputStream in = r.openInputStream();
          try {
            InputStreamReader rds = new InputStreamReader(in, Constants.DEFAULT_CHARSET);
            copy(rds, output);
            output.flush();
          } finally {
            in.close();
          }
        }
      }
    } finally {
      j.close();
    }

  }

  void doDot(String args[], int i) throws Exception {
    File out = getFile("graph.gv");
    Builder b = new Builder();

    for (; i < args.length; i++) {
      if ("-o".equals(args[i]))
        out = getFile(args[++i]);
      else if (args[i].startsWith("-"))
        error("Unknown option for dot: %s", args[i]);
      else
        b.addClasspath(getFile(args[i]));
    }
    b.setProperty(EXPORT_PACKAGE, "*");
    b.setPedantic(isPedantic());
    b.build();
    FileWriter os = new FileWriter(out);
    PrintWriter pw = new PrintWriter(os);
    try {
      pw.println("digraph bnd {");
      pw.println("  size=\"6,6\";");
      pw.println("node [color=lightblue2, style=filled,shape=box];");
      for (Map.Entry<String, Set<String>> uses : b.getUses().entrySet()) {
        for (String p : uses.getValue()) {
          if (!p.startsWith("java."))
            pw.printf("\"%s\" -> \"%s\";\n", uses.getKey(), p);
        }
      }
      pw.println("}");

    } finally {
      pw.close();
    }

  }

  private void traverse(List<File> files, File f) {
    if (f.isFile()) {
      if (f.getName().endsWith(".jar"))
        files.add(f);
    } else if (f.isDirectory()) {
      File[] subs = f.listFiles();
      for (File sub : subs) {
        traverse(files, sub);
      }
    }
  }

  public void global(String args[], int i) throws BackingStoreException {
    Settings settings = new Settings();

    if (args.length == i) {
      for (String key : settings.getKeys())
        System.out.printf("%-30s %s\n", key, settings.globalGet(key, "<>"));
    } else {
      while (i < args.length) {
        boolean remove = false;
        if ("-remove".equals(args[i])) {
          remove = true;
          i++;
        }
        if (i + 1 == args.length) {
          if (remove)
            settings.globalRemove(args[i]);
          else
            System.out.printf("%-30s %s\n", args[i], settings.globalGet(args[i], "<>"));
          i++;
        } else {
          settings.globalSet(args[i], args[i + 1]);
          i += 2;
        }
      }
    }
  }

  /**
   * Merge a bundle with its source.
   *
   * @throws Exception
   */

  public void doMerge(String args[], int i) throws Exception {
    File out = null;
    // String prefix = "";
    // boolean maven;

    List<Jar> sourcePath = new ArrayList<Jar>();
    while (i < args.length - 1) {
      String arg = args[i++];
      if (arg.equals("-o")) {
        out = getFile(arg);
      } else if (arg.equals("-maven")) {
        // maven = true;
      } else {
        File source = getFile(arg);
        if (source.exists()) {
          Jar jar = new Jar(source);
          addClose(jar);
          sourcePath.add(jar);
        } else {
          error("Sourec file/dir does not exist");
        }
      }
    }
    if (i >= args.length) {
      error("No binary file specified");
      return;
    }

    File binary = getFile(args[i]);
    Jar output = new Jar(binary);
    try {
      Analyzer analyzer = new Analyzer();
      analyzer.setJar(output);
      analyzer.analyze();

      outer: for (Clazz clazz : analyzer.getClassspace().values()) {
        String sourcename = clazz.getSourceFile();
        String path = clazz.getPath();
        int n = path.lastIndexOf('/');
        if (n >= 0) {
          path = path.substring(0, n + 1);
        } else
          path = "";

        String cname = path + sourcename;
        for (Jar source : sourcePath) {
          Resource r = source.getResource(cname);
          if (r != null) {
            output.putResource("OSGI-OPT/src/" + cname, r);
            continue outer;
          }
        }
        error("Source not found %s", cname);
      }

      if (out == null) {
        File backup = new File(binary.getAbsolutePath() + ".bak");
        binary.renameTo(backup);
        out = binary;
      }
      output.write(out);
    } finally {
      output.close();
      for (Jar jar : sourcePath)
        jar.close();
    }
  }

  /**
   * Create lib file on a directory.
   *
   * @throws Exception
   *
   * @throws Exception
   */

  public void doLib(String args[], int i) throws Exception {
    File out = null;
    List<File> files = new ArrayList<File>();

    while (i < args.length) {
      String arg = args[i++];
      if ("-o".equals(arg)) {
        out = getFile(args[i++]);
      } else if (arg.startsWith("-")) {
        error("Unknown option: %s", arg);
      } else
        files.add(getFile(arg));
    }
    if (files.isEmpty()) {
      error("No files specified for lib");
      return;
    }

    if (out == null) {
      out = getFile(files.get(0).getName() + ".lib");
    }

    System.out.println("Using " + out);
    Writer w = new FileWriter(out);
    try {
      for (File file : files) {
        System.out.println("And using " + file);
        if (file.isDirectory())
          doLib(file, w);
        else if (file.isFile())
          doSingleFileLib(file, w);
        else if (!file.equals(out))
          error("Not a file %s", file);
      }
    } finally {
      w.close();
    }
  }

  public void doLib(File dir, Appendable out) throws Exception {
    for (File file : dir.listFiles()) {
      doSingleFileLib(file, out);
    }
  }

  /**
   * @param out
   * @param file
   * @throws IOException
   * @throws Exception
   */
  void doSingleFileLib(File file, Appendable out) throws IOException, Exception {
    Jar jar = new Jar(file);
    String bsn = jar.getBsn();
    System.out.println(bsn);
    String version = jar.getVersion();
    jar.close();
    if (bsn == null) {
      error("No valid bsn for %s", file);
      bsn = "not set";
    }
    if (version == null)
      version = "0";

    Version v = new Version(version);
    v = new Version(v.getMajor(), v.getMinor(), v.getMicro());
    out.append(bsn);
    out.append(";version=" + v + "\n"); // '[" + v + "," + v + "]'\n");
  }

  /**
   * @return
   * @throws IOException
   * @throws MalformedURLException
   */
  protected Jar getJarFromFileOrURL(String spec) throws IOException, MalformedURLException {
    Jar jar;
    File jarFile = getFile(spec);
    if (jarFile.exists()) {
      jar = new Jar(jarFile);
    } else {
      URL url = new URL(spec);
      InputStream in = url.openStream();
      try {
        jar = new Jar(url.getFile(), in);
      } finally {
        in.close();
      }
    }
    addClose(jar);
    return jar;
  }

}
TOP

Related Classes of aQute.bnd.main.bnd

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.