Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java Thu 
Sep  6 18:28:06 2012
@@ -3,29 +3,204 @@ package aQute.lib.deployer;
 import java.io.*;
 import java.security.*;
 import java.util.*;
-import java.util.jar.*;
 import java.util.regex.*;
 
-import aQute.bnd.header.*;
 import aQute.bnd.osgi.*;
+import aQute.bnd.osgi.Verifier;
 import aQute.bnd.service.*;
 import aQute.bnd.version.*;
+import aQute.lib.collections.*;
+import aQute.lib.hex.*;
 import aQute.lib.io.*;
+import aQute.libg.command.*;
+import aQute.libg.cryptography.*;
+import aQute.libg.reporter.*;
 import aQute.service.reporter.*;
 
-public class FileRepo implements Plugin, RepositoryPlugin, Refreshable, 
RegistryPlugin {
-       public final static String      LOCATION        = "location";
-       public final static String      READONLY        = "readonly";
-       public final static String      NAME            = "name";
+/**
+ * A FileRepo is the primary and example implementation of a repository based 
on
+ * a file system. It maintains its files in a bsn/bsn-version.jar style from a
+ * given location. It implements all the functions of the
+ * {@link RepositoryPlugin}, {@link Refreshable}, {@link Actionable}, and
+ * {@link Closeable}. The FileRepo can be extended or used as is. When used as
+ * is, it is possible to add shell commands to the life cycle of the FileRepo.
+ * This life cycle is as follows:
+ * <ul>
+ * <li>{@link #CMD_INIT} - Is only executed when the location did not 
exist</li>
+ * <li>{@link #CMD_OPEN} - Called (after init if necessary) to open it 
once</li>
+ * <li>{@link #CMD_REFRESH} - Called when refreshed.</li>
+ * <li>{@link #CMD_BEFORE_PUT} - Before the file system is changed</li>
+ * <li>{@link #CMD_AFTER_PUT} - After the file system has changed, and the put
+ * <li>{@link #CMD_BEFORE_GET} - Before the file is gotten</li>
+ * <li>{@link #CMD_AFTER_ACTION} - Before the file is gotten</li>
+ * <li>{@link #CMD_CLOSE} - When the repo is closed and no more actions will
+ * take place</li> was a success</li>
+ * <li>{@link #CMD_ABORT_PUT} - When the put is aborted.</li>
+ * <li>{@link #CMD_CLOSE} - To close the repository.</li>
+ * </ul>
+ * Additionally, it is possible to set the {@link #CMD_SHELL} and the
+ * {@link #CMD_PATH}. Notice that you can use the ${global} macro to read 
global
+ * (that is, machine local) settings from the ~/.bnd/settings.json file (can be
+ * managed with bnd).
+ */
+public class FileRepo implements Plugin, RepositoryPlugin, Refreshable, 
RegistryPlugin, Actionable, Closeable {
 
-       File[]                                          EMPTY_FILES     = new 
File[0];
+       /**
+        * If set, will trace to stdout. Works only if no reporter is set.
+        */
+       public final static String      TRACE                           = 
"trace";
+
+       /**
+        * Property name for the location of the repo, must be a valid path name
+        * using forward slashes (see {@link IO#getFile(String)}.
+        */
+       public final static String      LOCATION                        = 
"location";
+
+       /**
+        * Property name for the readonly state of the repository. If no, will
+        * read/write, otherwise it must be a boolean value read by
+        * {@link Boolean#parseBoolean(String)}. Read only repositories will not
+        * accept writes.
+        */
+       public final static String      READONLY                        = 
"readonly";
+
+       /**
+        * Set the name of this repository (optional)
+        */
+       public final static String      NAME                            = 
"name";
+
+       /**
+        * Path property for commands. A comma separated path for directories 
to be
+        * searched for command. May contain $ @} which will be replaced by the
+        * system path. If this property is not set, the system path is assumed.
+        */
+       public static final String      CMD_PATH                        = 
"cmd.path";
+
+       /**
+        * The name ( and path) of the shell to execute the commands. By default
+        * this is sh and searched in the path.
+        */
+       public static final String      CMD_SHELL                       = 
"cmd.shell";
+
+       /**
+        * Property for commands. The command only runs when the location does 
not
+        * exist. </p>
+        * 
+        * @param rootFile
+        *            the root of the repo (directory exists)
+        */
+       public static final String      CMD_INIT                        = 
"cmd.init";
+
+       /**
+        * Property for commands. Command is run before the repo is first used. 
</p>
+        * 
+        * @param $0
+        *            rootFile the root of the repo (directory exists)
+        */
+       public static final String      CMD_OPEN                        = 
"cmd.open";
+
+       /**
+        * Property for commands. The command runs after a put operation. </p>
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        * @param $1
+        *            the file that was put
+        * @param $2
+        *            the hex checksum of the file
+        */
+       public static final String      CMD_AFTER_PUT           = 
"cmd.after.put";
+
+       /**
+        * Property for commands. The command runs when the repository is 
refreshed.
+        * </p>
+        * 
+        * @param $
+        *            {0} the root of the repo (directory exists)
+        */
+       public static final String      CMD_REFRESH                     = 
"cmd.refresh";
+
+       /**
+        * Property for commands. The command runs after the file is put. </p>
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        * @param $1
+        *            the path to a temporary file
+        */
+       public static final String      CMD_BEFORE_PUT          = 
"cmd.before.put";
+
+       /**
+        * Property for commands. The command runs when a put is aborted after 
file
+        * changes were made. </p>
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        * @param $1
+        *            the temporary file that was used (optional)
+        */
+       public static final String      CMD_ABORT_PUT           = 
"cmd.abort.put";
+
+       /**
+        * Property for commands. The command runs after the file is put. </p>
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        */
+       public static final String      CMD_CLOSE                       = 
"cmd.close";
+
+       /**
+        * Property for commands. Will be run after an action has been executed.
+        * </p>
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        * @param $1
+        *            the path to the file that the action was executed on
+        * @param $2
+        *            the action executed
+        */
+       public static final String      CMD_AFTER_ACTION        = 
"cmd.after.action";
+
+       /**
+        * Called before a before get.
+        * 
+        * @param $0
+        *            the root of the repo (directory exists)
+        * @param $1
+        *            the bsn
+        * @param $2
+        *            the version
+        */
+       public static final String      CMD_BEFORE_GET          = 
"cmd.before.get";
+
+       /**
+        * Options used when the options are null
+        */
+       static final PutOptions         DEFAULTOPTIONS          = new 
PutOptions();
+
+       String                                          shell;
+       String                                          path;
+       String                                          init;
+       String                                          open;
+       String                                          refresh;
+       String                                          beforePut;
+       String                                          afterPut;
+       String                                          abortPut;
+       String                                          beforeGet;
+       String                                          close;
+       String                                          action;
+
+       File[]                                          EMPTY_FILES             
        = new File[0];
        protected File                          root;
        Registry                                        registry;
-       boolean                                         canWrite        = true;
-       Pattern                                         REPO_FILE       = 
Pattern.compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+|latest)\\.(jar|lib)");
+       boolean                                         canWrite                
        = true;
+       Pattern                                         REPO_FILE               
        = Pattern.compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+)\\.(jar|lib)");
        Reporter                                        reporter;
        boolean                                         dirty;
        String                                          name;
+       boolean                                         inited;
+       boolean                                         trace;
 
        public FileRepo() {}
 
@@ -35,280 +210,200 @@ public class FileRepo implements Plugin,
                this.canWrite = canWrite;
        }
 
-       protected void init() throws Exception {
-               // for extensions
+       /**
+        * Initialize the repository Subclasses should first call this method 
and
+        * then if it returns true, do their own initialization
+        * 
+        * @return true if initialized, false if already had been initialized.
+        * @throws Exception
+        */
+       protected boolean init() throws Exception {
+               if (inited)
+                       return false;
+
+               inited = true;
+
+               if (reporter == null) {
+                       ReporterAdapter reporter = trace ? new 
ReporterAdapter(System.out) : new ReporterAdapter();
+                       reporter.setTrace(trace);
+                       reporter.setExceptions(trace);
+                       this.reporter = reporter;
+               }
+
+               if (!root.isDirectory()) {
+                       root.mkdirs();
+                       if (!root.isDirectory())
+                               throw new IllegalArgumentException("Location 
cannot be turned into a directory " + root);
+
+                       exec(init, root.getAbsolutePath());
+               }
+               open();
+               return true;
        }
 
+       /**
+        * @see aQute.bnd.service.Plugin#setProperties(java.util.Map)
+        */
        public void setProperties(Map<String,String> map) {
                String location = map.get(LOCATION);
                if (location == null)
                        throw new IllegalArgumentException("Location must be 
set on a FileRepo plugin");
 
-               root = new File(location);
-
+               root = IO.getFile(IO.home, location);
                String readonly = map.get(READONLY);
                if (readonly != null && 
Boolean.valueOf(readonly).booleanValue())
                        canWrite = false;
 
                name = map.get(NAME);
+               path = map.get(CMD_PATH);
+               shell = map.get(CMD_SHELL);
+               init = map.get(CMD_INIT);
+               open = map.get(CMD_OPEN);
+               refresh = map.get(CMD_REFRESH);
+               beforePut = map.get(CMD_BEFORE_PUT);
+               abortPut = map.get(CMD_ABORT_PUT);
+               afterPut = map.get(CMD_AFTER_PUT);
+               beforeGet = map.get(CMD_BEFORE_GET);
+               close = map.get(CMD_CLOSE);
+               action = map.get(CMD_AFTER_ACTION);
+
+               trace = map.get(TRACE) != null && 
Boolean.parseBoolean(map.get(TRACE));
        }
 
        /**
-        * Get a list of URLs to bundles that are constrained by the bsn and
-        * versionRange.
+        * Answer if this repository can write.
         */
-       private File[] get(String bsn, String versionRange) throws Exception {
-               init();
-
-               // If the version is set to project, we assume it is not
-               // for us. A project repo will then get it.
-               if (versionRange != null && versionRange.equals("project"))
-                       return null;
-
-               //
-               // Check if the entry exists
-               //
-               File f = new File(root, bsn);
-               if (!f.isDirectory())
-                       return null;
-
-               //
-               // The version range we are looking for can
-               // be null (for all) or a version range.
-               //
-               VersionRange range;
-               if (versionRange == null || versionRange.equals("latest")) {
-                       range = new VersionRange("0");
-               } else
-                       range = new VersionRange(versionRange);
-
-               //
-               // Iterator over all the versions for this BSN.
-               // Create a sorted map over the version as key
-               // and the file as URL as value. Only versions
-               // that match the desired range are included in
-               // this list.
-               //
-               File instances[] = f.listFiles();
-               SortedMap<Version,File> versions = new TreeMap<Version,File>();
-               for (int i = 0; i < instances.length; i++) {
-                       Matcher m = REPO_FILE.matcher(instances[i].getName());
-                       if (m.matches() && m.group(1).equals(bsn)) {
-                               String versionString = m.group(2);
-                               Version version;
-                               if (versionString.equals("latest"))
-                                       version = new 
Version(Integer.MAX_VALUE);
-                               else
-                                       version = new Version(versionString);
-
-                               if (range.includes(version) || 
versionString.equals(versionRange))
-                                       versions.put(version, instances[i]);
-                       }
-               }
-
-               File[] files = versions.values().toArray(EMPTY_FILES);
-               if ("latest".equals(versionRange) && files.length > 0) {
-                       return new File[] {
-                               files[files.length - 1]
-                       };
-               }
-               return files;
-       }
-
        public boolean canWrite() {
                return canWrite;
        }
 
-       protected PutResult putArtifact(File tmpFile, PutOptions options) 
throws Exception {
+       /**
+        * Local helper method that tries to insert a file in the repository. 
This
+        * method can be overridden but MUST not change the content of the 
tmpFile.
+        * This method should also create a latest version of the artifact for
+        * reference by tools like ant etc. </p> It is allowed to rename the 
file,
+        * the tmp file must be beneath the root directory to prevent rename
+        * problems.
+        * 
+        * @param tmpFile
+        *            source file
+        * @param digest
+        * @return a File that contains the content of the tmpFile
+        * @throws Exception
+        */
+       protected File putArtifact(File tmpFile, byte[] digest) throws 
Exception {
                assert (tmpFile != null);
-               assert (options != null);
 
-               Jar jar = null;
+               Jar jar = new Jar(tmpFile);
                try {
-                       init();
                        dirty = true;
 
-                       jar = new Jar(tmpFile);
-
-                       Manifest manifest = jar.getManifest();
-                       if (manifest == null)
-                               throw new IllegalArgumentException("No manifest 
in JAR: " + jar);
-
-                       String bsn = 
manifest.getMainAttributes().getValue(Analyzer.BUNDLE_SYMBOLICNAME);
+                       String bsn = jar.getBsn();
                        if (bsn == null)
-                               throw new IllegalArgumentException("No Bundle 
SymbolicName set");
-
-                       Parameters b = Processor.parseHeader(bsn, null);
-                       if (b.size() != 1)
-                               throw new IllegalArgumentException("Multiple 
bsn's specified " + b);
-
-                       for (String key : b.keySet()) {
-                               bsn = key;
-                               if 
(!Verifier.SYMBOLICNAME.matcher(bsn).matches())
-                                       throw new 
IllegalArgumentException("Bundle SymbolicName has wrong format: " + bsn);
-                       }
+                               throw new IllegalArgumentException("No bsn set 
in jar: " + tmpFile);
 
-                       String versionString = 
manifest.getMainAttributes().getValue(Analyzer.BUNDLE_VERSION);
-                       Version version;
+                       String versionString = jar.getVersion();
                        if (versionString == null)
-                               version = new Version();
-                       else
-                               version = new Version(versionString);
+                               versionString = "0";
+                       else if (!Verifier.isVersion(versionString))
+                               throw new IllegalArgumentException("Incorrect 
version in : " + tmpFile + " " + versionString);
 
-                       if (reporter != null)
-                               reporter.trace("bsn=%s version=%s", bsn, 
version);
+                       Version version = new Version(versionString);
+
+                       reporter.trace("bsn=%s version=%s", bsn, version);
 
                        File dir = new File(root, bsn);
-                       if (!dir.exists() && !dir.mkdirs()) {
+                       dir.mkdirs();
+                       if (!dir.isDirectory())
                                throw new IOException("Could not create 
directory " + dir);
-                       }
+
                        String fName = bsn + "-" + 
version.getWithoutQualifier() + ".jar";
                        File file = new File(dir, fName);
 
-                       boolean renamed = false;
-                       PutResult result = new PutResult();
+                       reporter.trace("updating %s ", file.getAbsolutePath());
 
-                       if (reporter != null)
-                               reporter.trace("updating %s ", 
file.getAbsolutePath());
-                       if (!file.exists() || file.lastModified() < 
jar.lastModified()) {
-                               if (file.exists()) {
-                                       IO.delete(file);
-                               }
-                               IO.rename(tmpFile, file);
-                               renamed = true;
-                               result.artifact = file.toURI();
+                       IO.rename(tmpFile, file);
 
-                               if (reporter != null)
-                                       reporter.progress(-1, "updated " + 
file.getAbsolutePath());
-
-                               fireBundleAdded(jar, file);
-                       } else {
-                               if (reporter != null) {
-                                       reporter.progress(-1, "Did not update " 
+ jar + " because repo has a newer version");
-                                       reporter.trace("NOT Updating " + fName 
+ " (repo is newer)");
-                               }
-                       }
+                       fireBundleAdded(jar, file);
+                       afterPut(file, bsn, version, Hex.toHexString(digest));
 
+                       // TODO like to beforeGet rid of the latest option. 
This is only
+                       // used to have a constant name for the outside users 
(like ant)
+                       // we should be able to handle this differently?
                        File latest = new File(dir, bsn + "-latest.jar");
-                       boolean latestExists = latest.exists() && 
latest.isFile();
-                       boolean latestIsOlder = latestExists && 
(latest.lastModified() < jar.lastModified());
-                       if ((options.createLatest && !latestExists) || 
latestIsOlder) {
-                               if (latestExists) {
-                                       IO.delete(latest);
-                               }
-                               if (!renamed) {
-                                       IO.rename(tmpFile, latest);
-                               } else {
-                                       IO.copy(file, latest);
-                               }
-                               result.latest = latest.toURI();
-                       }
+                       IO.copy(file, latest);
 
-                       return result;
+                       reporter.trace("updated %s", file.getAbsolutePath());
+
+                       return file;
                }
                finally {
-                       if (jar != null) {
-                               jar.close();
-                       }
+                       jar.close();
                }
        }
 
-       /* a straight copy of this method lives in LocalIndexedRepo */
+       /*
+        * (non-Javadoc)
+        * @see aQute.bnd.service.RepositoryPlugin#put(java.io.InputStream,
+        * aQute.bnd.service.RepositoryPlugin.PutOptions)
+        */
        public PutResult put(InputStream stream, PutOptions options) throws 
Exception {
-               /* both parameters are required */
-               if ((stream == null) || (options == null)) {
-                       throw new IllegalArgumentException("No stream and/or 
options specified");
-               }
-
                /* determine if the put is allowed */
                if (!canWrite) {
                        throw new IOException("Repository is read-only");
                }
 
-               /* the root directory of the repository has to be a directory */
-               if (!root.isDirectory()) {
-                       throw new IOException("Repository directory " + root + 
" is not a directory");
-               }
+               assert stream != null;
 
-               /* determine if the artifact needs to be verified */
-               boolean verifyFetch = (options.digest != null);
-               boolean verifyPut = !options.allowArtifactChange;
+               if (options == null)
+                       options = DEFAULTOPTIONS;
 
-               /* determine which digests are needed */
-               boolean needFetchDigest = verifyFetch || verifyPut;
-               boolean needPutDigest = verifyPut || options.generateDigest;
+               init();
 
                /*
-                * setup a new stream that encapsulates the stream and 
calculates (when
-                * needed) the digest
+                * copy the artifact from the (new/digest) stream into a 
temporary file
+                * in the root directory of the repository
                 */
-               DigestInputStream dis = new DigestInputStream(stream, 
MessageDigest.getInstance("SHA-1"));
-               dis.on(needFetchDigest);
-
-               File tmpFile = null;
+               File tmpFile = IO.createTempFile(root, "put", ".jar");
                try {
-                       /*
-                        * copy the artifact from the (new/digest) stream into 
a temporary
-                        * file in the root directory of the repository
-                        */
-                       tmpFile = IO.createTempFile(root, "put", ".bnd");
-                       IO.copy(dis, tmpFile);
-
-                       /* get the digest if available */
-                       byte[] disDigest = needFetchDigest ? 
dis.getMessageDigest().digest() : null;
-
-                       /* verify the digest when requested */
-                       if (verifyFetch && 
!MessageDigest.isEqual(options.digest, disDigest)) {
-                               throw new IOException("Retrieved artifact 
digest doesn't match specified digest");
-                       }
+                       DigestInputStream dis = new DigestInputStream(stream, 
MessageDigest.getInstance("SHA-1"));
+                       try {
+                               IO.copy(dis, tmpFile);
 
-                       /* put the artifact into the repository (from the 
temporary file) */
-                       PutResult r = putArtifact(tmpFile, options);
+                               byte[] digest = dis.getMessageDigest().digest();
 
-                       /* calculate the digest when requested */
-                       if (needPutDigest && (r.artifact != null)) {
-                               MessageDigest sha1 = 
MessageDigest.getInstance("SHA-1");
-                               IO.copy(new File(r.artifact), sha1);
-                               r.digest = sha1.digest();
-                       }
+                               if (options.digest != null && 
!Arrays.equals(digest, options.digest))
+                                       throw new IOException("Retrieved 
artifact digest doesn't match specified digest");
 
-                       /* verify the artifact when requested */
-                       if (verifyPut && (r.digest != null) && 
!MessageDigest.isEqual(disDigest, r.digest)) {
-                               File f = new File(r.artifact);
-                               if (f.exists()) {
-                                       IO.delete(f);
-                               }
-                               throw new IOException("Stored artifact digest 
doesn't match specified digest");
-                       }
+                               /*
+                                * put the artifact into the repository (from 
the temporary
+                                * file)
+                                */
+                               beforePut(tmpFile);
+                               File file = putArtifact(tmpFile, digest);
+                               file.setReadOnly();
 
-                       return r;
-               }
-               finally {
-                       if (tmpFile != null && tmpFile.exists()) {
-                               IO.delete(tmpFile);
-                       }
-               }
-       }
+                               PutResult result = new PutResult();
+                               result.digest = digest;
+                               result.artifact = file.toURI();
 
-       protected void fireBundleAdded(Jar jar, File file) {
-               if (registry == null)
-                       return;
-               List<RepositoryListenerPlugin> listeners = 
registry.getPlugins(RepositoryListenerPlugin.class);
-               for (RepositoryListenerPlugin listener : listeners) {
-                       try {
-                               listener.bundleAdded(this, jar, file);
+                               return result;
                        }
-                       catch (Exception e) {
-                               if (reporter != null)
-                                       reporter.warning("Repository listener 
threw an unexpected exception: %s", e);
+                       finally {
+                               dis.close();
                        }
                }
+               catch (Exception e) {
+                       abortPut(tmpFile);
+                       throw e;
+               }
+               finally {
+                       IO.delete(tmpFile);
+               }
        }
 
        public void setLocation(String string) {
-               root = new File(string);
-               if (!root.isDirectory())
-                       throw new IllegalArgumentException("Invalid repository 
directory");
+               root = IO.getFile(string);
        }
 
        public void setReporter(Reporter reporter) {
@@ -344,7 +439,7 @@ public class FileRepo implements Plugin,
                return result;
        }
 
-       public List<Version> versions(String bsn) throws Exception {
+       public SortedSet<Version> versions(String bsn) throws Exception {
                init();
                File dir = new File(root, bsn);
                if (dir.isDirectory()) {
@@ -359,9 +454,9 @@ public class FileRepo implements Plugin,
                                        list.add(new Version(version));
                                }
                        }
-                       return list;
+                       return new SortedList<Version>(list);
                }
-               return null;
+               return SortedList.empty();
        }
 
        @Override
@@ -373,7 +468,9 @@ public class FileRepo implements Plugin,
                return root;
        }
 
-       public boolean refresh() {
+       public boolean refresh() throws Exception {
+               init();
+               exec(refresh, root);
                if (dirty) {
                        dirty = false;
                        return true;
@@ -388,60 +485,290 @@ public class FileRepo implements Plugin,
                return name;
        }
 
-       public Jar get(String bsn, Version v) throws Exception {
+       /*
+        * (non-Javadoc)
+        * @see aQute.bnd.service.RepositoryPlugin#get(java.lang.String,
+        * aQute.bnd.version.Version, java.util.Map)
+        */
+       public File get(String bsn, Version version, Map<String,String> 
properties, DownloadListener... listeners)
+                       throws Exception {
                init();
-               File bsns = new File(root, bsn);
-               File version = new File(bsns, bsn + "-" + v.getMajor() + "." + 
v.getMinor() + "." + v.getMicro() + ".jar");
-               if (version.exists())
-                       return new Jar(version);
+               beforeGet(bsn, version);
+               File file = getLocal(bsn, version, properties);
+               if (file.exists()) {
+                       for (DownloadListener l : listeners) {
+                               try {
+                                       l.success(file);
+                               }
+                               catch (Exception e) {
+                                       reporter.exception(e, "Download 
listener for %s", file);
+                               }
+                       }
+                       return file;
+               }
                return null;
        }
 
-       public File get(String bsn, String version, Strategy strategy, 
Map<String,String> properties) throws Exception {
-               if (version == null)
-                       version = "0.0.0";
+       public void setRegistry(Registry registry) {
+               this.registry = registry;
+       }
 
-               if (strategy == Strategy.EXACT) {
-                       VersionRange vr = new VersionRange(version);
-                       if (vr.isRange())
-                               return null;
+       public String getLocation() {
+               return root.toString();
+       }
 
-                       if (vr.getHigh().getMajor() == Integer.MAX_VALUE)
-                               version = "latest";
+       public Map<String,Runnable> actions(Object... target) throws Exception {
+               if (target == null || target.length == 0)
+                       return null; // no default actions
 
-                       File file = IO.getFile(root, bsn + "/" + bsn + "-" + 
version + ".jar");
-                       if (file.isFile())
-                               return file;
-                       file = IO.getFile(root, bsn + "/" + bsn + "-" + version 
+ ".lib");
-                       if (file.isFile())
-                               return file;
-                       return null;
+               try {
+                       String bsn = (String) target[0];
+                       Version version = (Version) target[1];
 
+                       final File f = get(bsn, version, null);
+                       if (f == null)
+                               return null;
+
+                       Map<String,Runnable> actions = new 
HashMap<String,Runnable>();
+                       actions.put("Delete " + bsn + "-" + status(bsn, 
version), new Runnable() {
+                               public void run() {
+                                       IO.delete(f);
+                                       if (f.getParentFile().list().length == 
0)
+                                               IO.delete(f.getParentFile());
+                                       afterAction(f, "delete");
+                               };
+                       });
+                       return actions;
                }
-               File[] files = get(bsn, version);
-               if (files == null || files.length == 0)
+               catch (Exception e) {
                        return null;
+               }
+       }
+
+       protected void afterAction(File f, String key) {
+               exec(action, root, f, key);
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see aQute.bnd.service.Actionable#tooltip(java.lang.Object[])
+        */
+       @SuppressWarnings("unchecked")
+       public String tooltip(Object... target) throws Exception {
+               if (target == null || target.length == 0)
+                       return String.format("%s\n%s", getName(), root);
 
-               if (files.length >= 0) {
-                       switch (strategy) {
-                               case LOWEST :
-                                       return files[0];
-                               case HIGHEST :
-                                       return files[files.length - 1];
-                               case EXACT :
-                                       // TODO
-                                       break;
+               try {
+                       String bsn = (String) target[0];
+                       Version version = (Version) target[1];
+                       Map<String,String> map = null;
+                       if (target.length > 2)
+                               map = (Map<String,String>) target[2];
+
+                       File f = getLocal(bsn, version, map);
+                       String s = String.format("Path: %s\nSize: %s\nSHA1: 
%s", f.getAbsolutePath(), readable(f.length(), 0), SHA1
+                                       .digest(f).asHex());
+                       if (f.getName().endsWith(".lib") && f.isFile()) {
+                               s += "\n" + IO.collect(f);
                        }
+                       return s;
+
+               }
+               catch (Exception e) {
+                       return null;
+               }
+       }
+
+       /*
+        * (non-Javadoc)
+        * @see aQute.bnd.service.Actionable#title(java.lang.Object[])
+        */
+       public String title(Object... target) throws Exception {
+               if (target == null || target.length == 0)
+                       return getName();
+
+               if (target.length == 1 && target[0] instanceof String)
+                       return (String) target[0];
+
+               if (target.length == 2 && target[0] instanceof String && 
target[1] instanceof Version) {
+                       return status((String) target[0], (Version) target[1]);
                }
+
                return null;
        }
 
-       public void setRegistry(Registry registry) {
-               this.registry = registry;
+       protected File getLocal(String bsn, Version version, Map<String,String> 
properties) {
+               File dir = new File(root, bsn);
+
+               File fjar = new File(dir, bsn + "-" + 
version.getWithoutQualifier() + ".jar");
+               if (fjar.isFile())
+                       return fjar.getAbsoluteFile();
+
+               File flib = new File(dir, bsn + "-" + 
version.getWithoutQualifier() + ".lib");
+               if (flib.isFile())
+                       return flib.getAbsoluteFile();
+
+               return fjar.getAbsoluteFile();
        }
 
-       public String getLocation() {
-               return root.toString();
+       protected String status(String bsn, Version version) {
+               File file = getLocal(bsn, version, null);
+               StringBuilder sb = new StringBuilder(version.toString());
+               String del = " [";
+
+               if (file.getName().endsWith(".lib")) {
+                       sb.append(del).append("L");
+                       del = "";
+               }
+               if (!file.getName().endsWith(".jar")) {
+                       sb.append(del).append("?");
+                       del = "";
+               }
+               if (!file.isFile()) {
+                       sb.append(del).append("X");
+                       del = "";
+               }
+               if (file.length() == 0) {
+                       sb.append(del).append("0");
+                       del = "";
+               }
+               if (del.equals(""))
+                       sb.append("]");
+               return sb.toString();
+       }
+
+       private static String[] names   = {
+                       "bytes", "Kb", "Mb", "Gb"
+                                                                       };
+
+       private Object readable(long length, int n) {
+               if (length < 0)
+                       return "<invalid>";
+
+               if (length < 1024 || n >= names.length)
+                       return length + names[n];
+
+               return readable(length / 1024, n + 1);
+       }
+
+       public void close() throws IOException {
+               if (inited)
+                       exec(close, root.getAbsolutePath());
+       }
+
+       protected void open() {
+               exec(open, root.getAbsolutePath());
+       }
+
+       protected void beforePut(File tmp) {
+               exec(beforePut, root.getAbsolutePath(), tmp.getAbsolutePath());
+       }
+
+       protected void afterPut(File file, String bsn, Version version, String 
sha) {
+               exec(afterPut, root.getAbsolutePath(), file.getAbsolutePath(), 
sha);
+       }
+
+       protected void abortPut(File tmpFile) {
+               exec(abortPut, root.getAbsolutePath(), 
tmpFile.getAbsolutePath());
+       }
+
+       protected void beforeGet(String bsn, Version version) {
+               exec(beforeGet, root.getAbsolutePath(), bsn, version);
+       }
+
+       protected void fireBundleAdded(Jar jar, File file) {
+               if (registry == null)
+                       return;
+               List<RepositoryListenerPlugin> listeners = 
registry.getPlugins(RepositoryListenerPlugin.class);
+               for (RepositoryListenerPlugin listener : listeners) {
+                       try {
+                               listener.bundleAdded(this, jar, file);
+                       }
+                       catch (Exception e) {
+                               if (reporter != null)
+                                       reporter.warning("Repository listener 
threw an unexpected exception: %s", e);
+                       }
+               }
+       }
+
+       /**
+        * Execute a command. Used in different stages so that the repository 
can be
+        * synced with external tools.
+        * 
+        * @param line
+        * @param target
+        */
+       void exec(String line, Object... args) {
+               if (line == null)
+                       return;
+
+               try {
+                       if (args != null)
+                               for (int i = 0; i < args.length; i++) {
+                                       if (i == 0)
+                                               line = 
line.replaceAll("\\$\\{@\\}", args[0].toString());
+                                       line = line.replaceAll("\\$" + i, 
args[i].toString());
+                               }
+
+                       if (shell == null) {
+                               shell = 
System.getProperty("os.name").toLowerCase().indexOf("win") > 0 ? "cmd.exe" : 
"sh";
+                       }
+                       Command cmd = new Command(shell);
+
+                       if (path != null) {
+                               cmd.inherit();
+                               String oldpath = cmd.var("PATH");
+                               path = path.replaceAll("\\s*,\\s*", 
File.pathSeparator);
+                               path = path.replaceAll("\\$\\{@\\}", oldpath);
+                               cmd.var("PATH", path);
+                       }
+
+                       cmd.setCwd(getRoot());
+                       StringBuilder stdout = new StringBuilder();
+                       StringBuilder stderr = new StringBuilder();
+                       int result = cmd.execute(line, stdout, stderr);
+                       if (result != 0) {
+                               reporter.error("Command %s failed with %s %s 
%s", line, result, stdout, stderr);
+                       }
+               }
+               catch (Exception e) {
+                       e.printStackTrace();
+                       reporter.exception(e, e.getMessage());
+               }
+       }
+
+       /*
+        * 8 Set the root directory directly
+        */
+       public void setDir(File repoDir) {
+               this.root = repoDir;
+       }
+
+       /**
+        * Delete an entry from the repository and cleanup the directory
+        * 
+        * @param bsn
+        * @param version
+        * @throws Exception
+        */
+       public void delete(String bsn, Version version) throws Exception {
+               assert bsn != null;
+
+               SortedSet<Version> versions;
+               if (version == null)
+                       versions = versions(bsn);
+               else
+                       versions = new SortedList<Version>(version);
+
+               for (Version v : versions) {
+                       File f = getLocal(bsn, version, null);
+                       if (!f.isFile())
+                               reporter.error("No artifact found for %s:%s", 
bsn, version);
+                       else
+                               IO.delete(f);
+               }
+               if ( versions(bsn).isEmpty())
+                       IO.delete( new File(root,bsn));
        }
 
 }

Modified: felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/io/IO.java Thu Sep  6 
18:28:06 2012
@@ -7,6 +7,8 @@ import java.security.*;
 import java.util.*;
 
 public class IO {
+       static public File      work    = new 
File(System.getProperty("user.dir"));
+       static public File      home    = new 
File(System.getProperty("user.home"));
 
        public static void copy(Reader r, Writer w) throws IOException {
                try {
@@ -226,7 +228,7 @@ public class IO {
 
        /**
         * Create a temporary file.
-        *
+        * 
         * @param directory
         *            the directory in which to create the file. Can be null, in
         *            which case the system TMP directory is used
@@ -257,16 +259,32 @@ public class IO {
        }
 
        public static File getFile(String filename) {
-               return new File(filename.replace("/", File.separator));
+               return getFile(work, filename);
        }
-       
+
        public static File getFile(File base, String file) {
+
+               if (file.startsWith("~/")) {
+                       file = file.substring(2);
+                       if (!file.startsWith("~/")) {
+                               return getFile(home, file);
+                       }
+               }
+               if (file.startsWith("~")) {
+                       file = file.substring(1);
+                       return getFile(home.getParentFile(), file);
+               }
+
                File f = new File(file);
                if (f.isAbsolute())
                        return f;
                int n;
 
+               if (base == null)
+                       base = work;
+
                f = base.getAbsoluteFile();
+
                while ((n = file.indexOf('/')) > 0) {
                        String first = file.substring(0, n);
                        file = file.substring(n + 1);
@@ -280,28 +298,35 @@ public class IO {
                return new File(f, file).getAbsoluteFile();
        }
 
-       /** Deletes the specified file.
-        * Folders are recursively deleted.<br>
+       /**
+        * Deletes the specified file. Folders are recursively deleted.<br>
         * If file(s) cannot be deleted, no feedback is provided (fail 
silently).
-        * @param f file to be deleted
+        * 
+        * @param f
+        *            file to be deleted
         */
        public static void delete(File f) {
                try {
                        deleteWithException(f);
-               } catch (IOException e) {
+               }
+               catch (IOException e) {
                        // Ignore a failed delete
                }
        }
-       
-       /** Deletes the specified file.
-        * Folders are recursively deleted.<br>
+
+       /**
+        * Deletes the specified file. Folders are recursively deleted.<br>
         * Throws exception if any of the files could not be deleted.
-        * @param f file to be deleted
-        * @throws IOException if the file (or contents of a folder) could not 
be deleted
+        * 
+        * @param f
+        *            file to be deleted
+        * @throws IOException
+        *             if the file (or contents of a folder) could not be 
deleted
         */
        public static void deleteWithException(File f) throws IOException {
                f = f.getAbsoluteFile();
-               if (!f.exists()) return;
+               if (!f.exists())
+                       return;
                if (f.getParentFile() == null)
                        throw new IllegalArgumentException("Cannot recursively 
delete root for safety reasons");
 
@@ -311,7 +336,8 @@ public class IO {
                        for (File sub : subs) {
                                try {
                                        deleteWithException(sub);
-                               } catch (IOException e) {
+                               }
+                               catch (IOException e) {
                                        wasDeleted = false;
                                }
                        }
@@ -323,19 +349,25 @@ public class IO {
                }
        }
 
-    /** Deletes <code>to</code> file if it exists, and renames 
<code>from</code> file to <code>to</code>.<br>
-     * Throws exception the rename operation fails.
-     * @param from source file
-     * @param to destination file
-     * @throws IOException if the rename operation fails
-     */
-    public static void rename(File from, File to) throws IOException {
-       IO.deleteWithException(to);
-       
-       boolean renamed = from.renameTo(to);
-       if (!renamed) throw new IOException("Could not rename " + 
from.getAbsoluteFile() + " to " + to.getAbsoluteFile());
-    }
+       /**
+        * Deletes <code>to</code> file if it exists, and renames 
<code>from</code>
+        * file to <code>to</code>.<br>
+        * Throws exception the rename operation fails.
+        * 
+        * @param from
+        *            source file
+        * @param to
+        *            destination file
+        * @throws IOException
+        *             if the rename operation fails
+        */
+       public static void rename(File from, File to) throws IOException {
+               IO.deleteWithException(to);
 
+               boolean renamed = from.renameTo(to);
+               if (!renamed)
+                       throw new IOException("Could not rename " + 
from.getAbsoluteFile() + " to " + to.getAbsoluteFile());
+       }
 
        public static long drain(InputStream in) throws IOException {
                long result = 0;

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java 
Thu Sep  6 18:28:06 2012
@@ -4,17 +4,17 @@ import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
-import aQute.lib.base64.*;
 import aQute.lib.hex.*;
 
+/**
+ * 
+ * Will now use hex for encoding byte arrays
+ *
+ */
 public class ByteArrayHandler extends Handler {
-
        @Override
        void encode(Encoder app, Object object, Map<Object,Type> visited) 
throws IOException, Exception {
-               if ( app.codec.isHex())
-                       StringHandler.string(app, Hex.toHexString((byte[]) 
object));
-               else
-                       StringHandler.string(app, Base64.encodeBase64((byte[]) 
object));
+               StringHandler.string(app, Hex.toHexString((byte[]) object));
        }
 
        @Override
@@ -31,8 +31,6 @@ public class ByteArrayHandler extends Ha
 
        @Override
        Object decode(Decoder dec, String s) throws Exception {
-               if ( dec.codec.isHex())
-                       return Hex.toByteArray(s);
-               return Base64.decodeBase64(s);
+               return Hex.toByteArray(s);
        }
 }

Modified: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java Thu 
Sep  6 18:28:06 2012
@@ -34,6 +34,8 @@ import java.util.regex.*;
  * <p/>
  * This Codec class can be used in a concurrent environment. The Decoders and
  * Encoders, however, must only be used in a single thread.
+ * <p/>
+ * Will now use hex for encoding byte arrays
  */
 public class JSONCodec {
        final static String                                                     
        START_CHARACTERS        = "[{\"-0123456789tfn";
@@ -51,7 +53,6 @@ public class JSONCodec {
        private static ByteArrayHandler                                 byteh   
                        = new ByteArrayHandler();
 
        boolean                                                                 
                ignorenull;
-       boolean                                                                 
                useHex;
 
        /**
         * Create a new Encoder with the state and appropriate API.
@@ -488,19 +489,4 @@ public class JSONCodec {
                return ignorenull;
        }
 
-       /**
-        * Use hex instead of default base 64 encoding
-        * 
-        * @param useHex
-        * @return
-        */
-       public JSONCodec setHex(boolean useHex) {
-               this.useHex = useHex;
-               return this;
-       }
-
-       public boolean isHex() {
-               return useHex;
-       }
-
 }
\ No newline at end of file

Modified: felix/trunk/bundleplugin/src/main/java/aQute/lib/json/packageinfo
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/json/packageinfo?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/json/packageinfo (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/json/packageinfo Thu Sep  
6 18:28:06 2012
@@ -1 +1 @@
-version 2.4.0
+version 3.0.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java?rev=1381708&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java 
(added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java Thu 
Sep  6 18:28:06 2012
@@ -0,0 +1,251 @@
+package aQute.lib.settings;
+
+import java.io.*;
+import java.security.*;
+import java.security.spec.*;
+import java.util.*;
+
+import aQute.lib.io.*;
+import aQute.lib.json.*;
+
+/**
+ * Maintains persistent settings for bnd (or other apps). The default is
+ * ~/.bnd/settings.json). The settings are normal string properties but it
+ * specially maintains a public/private key pair and it provides a method to
+ * sign a byte array with this pair.
+ * <p/>
+ * Why not keystore and preferences? Well, keystore is hard to use (you can 
only
+ * store a private key when you have a certificate, but you cannot create a
+ * certificate without using com.sun classes) and preferences are not editable.
+ */
+public class Settings implements Map<String,String> {
+       static JSONCodec        codec   = new JSONCodec();
+
+       private File            where;
+       private PublicKey       publicKey;
+       private PrivateKey      privateKey;
+       private boolean         loaded;
+       private boolean         dirty;
+
+       public static class Data {
+               public int                                      version = 1;
+               public byte[]                           secret;
+               public byte[]                           id;
+               public Map<String,String>       map             = new 
HashMap<String,String>();
+       }
+
+       Data    data    = new Data();
+
+       public Settings() {
+               this("~/.bnd/settings.json");
+       }
+
+       public Settings(String where) {
+               assert where != null;
+               this.where = IO.getFile(IO.work, where);
+       }
+
+       public boolean load() {
+               if (this.where.isFile() && this.where.length() > 1) {
+                       try {
+                               data = 
codec.dec().from(this.where).get(Data.class);
+                               loaded = true;
+                               return true;
+                       }
+                       catch (Exception e) {
+                               throw new RuntimeException("Cannot read 
settings file " + this.where, e);
+                       }
+               }
+
+               if (!data.map.containsKey("name"))
+                       data.map.put("name", System.getProperty("user.name"));
+               return false;
+       }
+
+       private void check() {
+               if (loaded)
+                       return;
+               load();
+               loaded = true;
+       }
+
+       public void save() {
+               if (!this.where.getParentFile().isDirectory() && 
!this.where.getParentFile().mkdirs())
+                       throw new RuntimeException("Cannot create directory in 
" + this.where.getParent());
+
+               try {
+                       codec.enc().to(this.where).put(data).flush();
+                       assert this.where.isFile();
+               }
+               catch (Exception e) {
+                       throw new RuntimeException("Cannot write settings file 
" + this.where, e);
+               }
+       }
+
+       public void generate() throws Exception {
+               KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+               SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+               keyGen.initialize(1024, random);
+               KeyPair pair = keyGen.generateKeyPair();
+               privateKey = pair.getPrivate();
+               publicKey = pair.getPublic();
+               data.secret = privateKey.getEncoded();
+               data.id = publicKey.getEncoded();
+               save();
+       }
+
+       public String getEmail() {
+               return get("email");
+       }
+
+       public void setEmail(String email) {
+               put("email", email);
+       }
+
+       public void setName(String v) {
+               put("name", v);
+       }
+
+       public String getName() {
+               String name = get("name");
+               if (name != null)
+                       return name;
+               return System.getProperty("user.name");
+       }
+
+       /**
+        * Return an encoded public RSA key. this key can be decoded with an
+        * X509EncodedKeySpec
+        * 
+        * @return an encoded public key.
+        * @throws Exception
+        */
+       public byte[] getPublicKey() throws Exception {
+               initKeys();
+               return data.id;
+       }
+
+       /**
+        * Return an encoded private RSA key. this key can be decoded with an
+        * PKCS8EncodedKeySpec
+        * 
+        * @return an encoded private key.
+        * @throws Exception
+        */
+       public byte[] getPrivateKey() throws Exception {
+               initKeys();
+               return data.secret;
+       }
+
+       /*
+        * Initialize the keys.
+        */
+       private void initKeys() throws Exception {
+               check();
+               if (publicKey != null)
+                       return;
+
+               if (data.id == null || data.secret == null) {
+                       generate();
+               } else {
+                       PKCS8EncodedKeySpec privateKeySpec = new 
PKCS8EncodedKeySpec(data.secret);
+                       X509EncodedKeySpec publicKeySpec = new 
X509EncodedKeySpec(data.id);
+                       KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                       privateKey = keyFactory.generatePrivate(privateKeySpec);
+                       publicKey = keyFactory.generatePublic(publicKeySpec);
+               }
+       }
+
+       /**
+        * Sign a byte array
+        */
+       public byte[] sign(byte[] con) throws Exception {
+               initKeys();
+
+               Signature hmac = Signature.getInstance("SHA1withRSA");
+               hmac.initSign(privateKey);
+               hmac.update(con);
+               return hmac.sign();
+       }
+
+       /**
+        * Verify a signed byte array
+        */
+       public boolean verify(byte[] con) throws Exception {
+               initKeys();
+
+               Signature hmac = Signature.getInstance("SHA1withRSA");
+               hmac.initVerify(publicKey);
+               hmac.update(con);
+               return hmac.verify(con);
+       }
+
+       public void clear() {
+               data = new Data();
+               IO.delete(where);
+       }
+
+       public boolean containsKey(Object key) {
+               check();
+               return data.map.containsKey(key);
+       }
+
+       public boolean containsValue(Object value) {
+               check();
+               return data.map.containsValue(value);
+       }
+
+       public Set<java.util.Map.Entry<String,String>> entrySet() {
+               check();
+               return data.map.entrySet();
+       }
+
+       public String get(Object key) {
+               check();
+
+               return data.map.get(key);
+       }
+
+       public boolean isEmpty() {
+               check();
+               return data.map.isEmpty();
+       }
+
+       public Set<String> keySet() {
+               check();
+               return data.map.keySet();
+       }
+
+       public String put(String key, String value) {
+               check();
+               dirty = true;
+               return data.map.put(key, value);
+       }
+
+       public void putAll(Map< ? extends String, ? extends String> v) {
+               check();
+               dirty = true;
+               data.map.putAll(v);
+       }
+
+       public String remove(Object key) {
+               check();
+               dirty = true;
+               return data.map.remove(key);
+       }
+
+       public int size() {
+               check();
+               return data.map.size();
+       }
+
+       public Collection<String> values() {
+               check();
+               return data.map.values();
+       }
+
+       public boolean isDirty() {
+               return dirty;
+       }
+
+}

Propchange: 
felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/Settings.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/packageinfo
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/packageinfo?rev=1381708&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/packageinfo 
(added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/lib/settings/packageinfo Thu 
Sep  6 18:28:06 2012
@@ -0,0 +1 @@
+version 1.1
\ No newline at end of file

Modified: felix/trunk/bundleplugin/src/main/java/aQute/libg/command/Command.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/command/Command.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/libg/command/Command.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/libg/command/Command.java Thu 
Sep  6 18:28:06 2012
@@ -68,7 +68,7 @@ public class Command {
 
                if (timeout != 0) {
                        timer = new TimerTask() {
-                               @Override
+                               //@Override TODO why did this not work? 
TimerTask implements Runnable
                                public void run() {
                                        timedout = true;
                                        process.destroy();

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java 
(original)
+++ 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java 
Thu Sep  6 18:28:06 2012
@@ -48,4 +48,13 @@ public abstract class Digester<T extends
        public abstract T digest(byte[] bytes) throws Exception;
 
        public abstract String getAlgorithm();
+
+       public T from(File f) throws Exception {
+               IO.copy(f, this);
+               return digest();
+       }
+       public T from(byte[] f) throws Exception {
+               IO.copy(f, this);
+               return digest();
+       }
 }

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java Thu 
Sep  6 18:28:06 2012
@@ -36,6 +36,13 @@ public class MD5 extends Digest {
        }
 
        public static MD5 digest(byte [] data) throws Exception {
-               return getDigester().digest(data);
+               return getDigester().from(data);
+       }
+
+       public static MD5 digest(File f) throws NoSuchAlgorithmException, 
Exception {
+               return getDigester().from(f);
+       }
+       public static MD5 digest(InputStream f) throws 
NoSuchAlgorithmException, Exception {
+               return getDigester().from(f);
        }
 }
\ No newline at end of file

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java 
Thu Sep  6 18:28:06 2012
@@ -36,6 +36,13 @@ public class SHA1 extends Digest {
        }
 
        public static SHA1 digest(byte [] data) throws Exception {
-               return getDigester().digest(data);
+               return getDigester().from(data);
+       }
+
+       public static SHA1 digest(File f) throws NoSuchAlgorithmException, 
Exception {
+               return getDigester().from(f);
+       }
+       public static SHA1 digest(InputStream f) throws 
NoSuchAlgorithmException, Exception {
+               return getDigester().from(f);
        }
 }
\ No newline at end of file

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java 
Thu Sep  6 18:28:06 2012
@@ -35,7 +35,15 @@ public class SHA256 extends Digest {
                return ALGORITHM;
        }
 
+
        public static SHA256 digest(byte [] data) throws Exception {
-               return getDigester().digest(data);
+               return getDigester().from(data);
+       }
+
+       public static SHA256 digest(File f) throws NoSuchAlgorithmException, 
Exception {
+               return getDigester().from(f);
+       }
+       public static SHA256 digest(InputStream f) throws 
NoSuchAlgorithmException, Exception {
+               return getDigester().from(f);
        }
 }
\ No newline at end of file

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- 
felix/trunk/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java 
(original)
+++ 
felix/trunk/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java 
Thu Sep  6 18:28:06 2012
@@ -82,7 +82,7 @@ public class QuotedTokenizer {
                        c = string.charAt(index++);
                        if (c == quote)
                                break;
-                       if (c == '\\' && index < string.length() && 
string.charAt(index + 1) == quote)
+                       if (c == '\\' && index < string.length() && 
string.charAt(index) == quote)
                                c = string.charAt(index++);
                        sb.append(c);
                }

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- 
felix/trunk/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java 
(original)
+++ 
felix/trunk/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java 
Thu Sep  6 18:28:06 2012
@@ -246,20 +246,9 @@ public class ReporterAdapter implements 
                return getInfo(other,null);
        }
        public boolean getInfo(Report other, String prefix) {
-               boolean ok = true;
-               if ( prefix == null)
-                       prefix = "";
-               else
-                       prefix = prefix + ": ";
-               for ( String error : other.getErrors()) {
-                       errors.add( prefix + error);
-                       ok = false;
-               }
-               
-               for ( String warning : other.getWarnings()) {
-                       warnings.add( prefix + warning);
-               }
-               return ok;
+               addErrors(prefix, other.getErrors());
+               addWarnings(prefix, other.getWarnings());
+               return other.isOk();
        }
 
        public Location getLocation(String msg) {
@@ -285,4 +274,31 @@ public class ReporterAdapter implements 
        public <T> T getMessages(Class<T> c) {
                return ReporterMessages.base(this, c);
        }
+       
+       /**
+        * Add a number of errors
+        */
+       
+       public void addErrors( String prefix, Collection<String> errors) {
+               if ( prefix == null)
+                       prefix = "";
+               else
+                       prefix = prefix + ": ";
+               for ( String s: errors) {
+                       this.errors.add( prefix + s);
+               }
+       }
+       /**
+        * Add a number of warnings
+        */
+       
+       public void addWarnings( String prefix, Collection<String> warnings) {
+               if ( prefix == null)
+                       prefix = "";
+               else
+                       prefix = prefix + ": ";
+               for ( String s: warnings) {
+                       this.warnings.add( prefix  + s);
+               }
+       }
 }

Modified: 
felix/trunk/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java?rev=1381708&r1=1381707&r2=1381708&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java 
(original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java 
Thu Sep  6 18:28:06 2012
@@ -268,7 +268,7 @@ public class ReplacerAdapter extends Rep
                        }
                        catch (InvocationTargetException e) {
                                if (e.getCause() instanceof 
IllegalArgumentException) {
-                                       reporter.error("%s, for cmd: %s, 
arguments; %s", e.getMessage(), method, Arrays.toString(args));
+                                       reporter.error("%s, for cmd: %s, 
arguments; %s", e.getCause().getMessage(), method, Arrays.toString(args));
                                } else {
                                        reporter.warning("Exception in replace: 
" + e.getCause());
                                        e.getCause().printStackTrace();


Reply via email to