Temporarily include bndlib 1.47 for testing purposes (not yet on central)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/libg/command/Command.java b/bundleplugin/src/main/java/aQute/libg/command/Command.java
new file mode 100644
index 0000000..bae82eb
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/command/Command.java
@@ -0,0 +1,194 @@
+package aQute.libg.command;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import aQute.libg.reporter.*;
+
+public class Command {
+
+ boolean trace;
+ Reporter reporter;
+ List<String> arguments = new ArrayList<String>();
+ long timeout = 0;
+ File cwd = new File("").getAbsoluteFile();
+ static Timer timer = new Timer();
+ Process process;
+ volatile boolean timedout;
+
+ public int execute(Appendable stdout, Appendable stderr) throws Exception {
+ return execute((InputStream) null, stdout, stderr);
+ }
+
+ public int execute(String input, Appendable stdout, Appendable stderr) throws Exception {
+ InputStream in = new ByteArrayInputStream(input.getBytes("UTF-8"));
+ return execute(in, stdout, stderr);
+ }
+
+ public int execute(InputStream in, Appendable stdout, Appendable stderr) throws Exception {
+ int result;
+ if (reporter != null) {
+ reporter.trace("executing cmd: %s", arguments);
+ }
+
+ String args[] = arguments.toArray(new String[arguments.size()]);
+
+ process = Runtime.getRuntime().exec(args, null, cwd);
+
+ // Make sure the command will not linger when we go
+ Runnable r = new Runnable() {
+ public void run() {
+ process.destroy();
+ }
+ };
+ Thread hook = new Thread(r, arguments.toString());
+ Runtime.getRuntime().addShutdownHook(hook);
+ TimerTask timer = null;
+ OutputStream stdin = process.getOutputStream();
+ final InputStreamHandler handler = in != null ? new InputStreamHandler(in, stdin) : null;
+
+ if (timeout != 0) {
+ timer = new TimerTask() {
+ public void run() {
+ timedout = true;
+ process.destroy();
+ if (handler != null)
+ handler.interrupt();
+ }
+ };
+ Command.timer.schedule(timer, timeout);
+ }
+
+ InputStream out = process.getInputStream();
+ try {
+ InputStream err = process.getErrorStream();
+ try {
+ new Collector(out, stdout).start();
+ new Collector(err, stdout).start();
+ if (handler != null)
+ handler.start();
+
+ result = process.waitFor();
+ } finally {
+ err.close();
+ }
+ } finally {
+ out.close();
+ if (timer != null)
+ timer.cancel();
+ Runtime.getRuntime().removeShutdownHook(hook);
+ if (handler != null)
+ handler.interrupt();
+ }
+ if (reporter != null)
+ reporter.trace("cmd %s executed with result=%d, result: %s/%s", arguments, result,
+ stdout, stderr);
+
+ if( timedout )
+ return Integer.MIN_VALUE;
+ byte exitValue = (byte) process.exitValue();
+ return exitValue;
+ }
+
+ public void add(String... args) {
+ for (String arg : args)
+ arguments.add(arg);
+ }
+
+ public void addAll(Collection<String> args) {
+ arguments.addAll(args);
+ }
+
+ public void setTimeout(long duration, TimeUnit unit) {
+ timeout = unit.toMillis(duration);
+ }
+
+ public void setTrace() {
+ this.trace = true;
+ }
+
+ public void setReporter(Reporter reporter) {
+ this.reporter = reporter;
+ }
+
+ public void setCwd(File dir) {
+ if (!dir.isDirectory())
+ throw new IllegalArgumentException("Working directory must be a directory: " + dir);
+
+ this.cwd = dir;
+ }
+
+ public void cancel() {
+ process.destroy();
+ }
+
+ class Collector extends Thread {
+ final InputStream in;
+ final Appendable sb;
+
+ public Collector(InputStream inputStream, Appendable sb) {
+ this.in = inputStream;
+ this.sb = sb;
+ }
+
+ public void run() {
+ try {
+ int c = in.read();
+ while (c >= 0) {
+ if (trace)
+ System.out.print((char) c);
+ sb.append((char) c);
+ c = in.read();
+ }
+ } catch (Exception e) {
+ try {
+ sb.append("\n**************************************\n");
+ sb.append(e.toString());
+ sb.append("\n**************************************\n");
+ } catch (IOException e1) {
+ }
+ if (reporter != null) {
+ reporter.trace("cmd exec: %s", e);
+ }
+ }
+ }
+ }
+
+ class InputStreamHandler extends Thread {
+ final InputStream in;
+ final OutputStream stdin;
+
+ public InputStreamHandler(InputStream in, OutputStream stdin) {
+ this.stdin = stdin;
+ this.in = in;
+ }
+
+ public void run() {
+ try {
+ int c = in.read();
+ while (c >= 0) {
+ stdin.write(c);
+ stdin.flush();
+ c = in.read();
+ }
+ } catch (InterruptedIOException e) {
+ // Ignore here
+ } catch (Exception e) {
+ // Who cares?
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+
+ for (String argument : arguments) {
+ sb.append(del);
+ sb.append(argument);
+ del = " ";
+ }
+ return sb.toString();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/command/packageinfo b/bundleplugin/src/main/java/aQute/libg/command/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/command/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java
new file mode 100644
index 0000000..630597f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Crypto.java
@@ -0,0 +1,67 @@
+package aQute.libg.cryptography;
+
+import java.math.*;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.util.regex.*;
+
+public class Crypto {
+ static final Pattern RSA_PRIVATE = Pattern
+ .compile("\\s*RSA.Private\\((\\p{xDigit})+:(\\p{xDigit})+\\)\\s*");
+ static final Pattern RSA_PUBLIC = Pattern
+ .compile("\\s*RSA.Public\\((\\p{xDigit})+:(\\p{xDigit})+\\)\\s*");
+
+ /**
+ *
+ * @param <T>
+ * @param spec
+ * @return
+ * @throws Exception
+ */
+ @SuppressWarnings("unchecked") public static <T> T fromString(String spec, Class<T> c) throws Exception {
+ if ( PrivateKey.class.isAssignableFrom(c)) {
+ Matcher m = RSA_PRIVATE.matcher(spec);
+ if ( m.matches()) {
+ return (T) RSA.createPrivate(
+ new BigInteger(m.group(1)), new BigInteger(m.group(2)));
+ }
+ throw new IllegalArgumentException("No such private key " + spec );
+ }
+
+ if ( PublicKey.class.isAssignableFrom(c)) {
+ Matcher m = RSA_PUBLIC.matcher(spec);
+ if ( m.matches()) {
+ return (T) RSA.create( new RSAPublicKeySpec(
+ new BigInteger(m.group(1)), new BigInteger(m.group(2))));
+ }
+ throw new IllegalArgumentException("No such public key " + spec );
+ }
+ return null;
+ }
+
+ public static String toString( Object key ) {
+ if ( key instanceof RSAPrivateKey ) {
+ RSAPrivateKey pk = (RSAPrivateKey) key;
+ return "RSA.Private(" + pk.getModulus() + ":" + pk.getPrivateExponent() + ")";
+ }
+ if ( key instanceof RSAPublicKey ) {
+ RSAPublicKey pk = (RSAPublicKey) key;
+ return "RSA.Private(" + pk.getModulus() + ":" + pk.getPublicExponent() + ")";
+ }
+ return null;
+ }
+
+
+ public static <T extends Digest> Signer<T> signer(PrivateKey key, Digester<T> digester) throws NoSuchAlgorithmException {
+ Signature s = Signature.getInstance(key.getAlgorithm() + "with" + digester.getAlgorithm());
+ return new Signer<T>(s,digester);
+ }
+
+ public static Verifier verifier(PublicKey key, Digest digest) throws NoSuchAlgorithmException {
+ Signature s = Signature.getInstance(key.getAlgorithm() + "with" + digest.getAlgorithm());
+ return new Verifier(s,digest);
+ }
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Digest.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Digest.java
new file mode 100644
index 0000000..f70caa0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Digest.java
@@ -0,0 +1,25 @@
+package aQute.libg.cryptography;
+
+import aQute.lib.hex.*;
+
+public abstract class Digest {
+ final byte[] digest;
+
+ protected Digest(byte[] checksum, int width) {
+ this.digest = checksum;
+ if (digest.length != width)
+ throw new IllegalArgumentException("Invalid width for digest: " + digest.length
+ + " expected " + width);
+ }
+
+
+ public byte[] digest() {
+ return digest;
+ }
+
+ @Override public String toString() {
+ return String.format("%s(d=%s)", getAlgorithm(), Hex.toHexString(digest));
+ }
+
+ public abstract String getAlgorithm();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java
new file mode 100644
index 0000000..50b9659
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java
@@ -0,0 +1,35 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+import aQute.lib.io.*;
+
+public abstract class Digester<T extends Digest> extends OutputStream {
+ protected MessageDigest md;
+
+ public Digester(MessageDigest instance){
+ md = instance;
+ }
+ @Override
+ public void write( byte[] buffer, int offset, int length) throws IOException{
+ md.update(buffer,offset,length);
+ }
+ @Override
+ public void write( int b) throws IOException{
+ md.update((byte) b);
+ }
+
+ public MessageDigest getMessageDigest() throws Exception {
+ return md;
+ }
+
+ public T from(InputStream in) throws Exception {
+ IO.copy(in,this);
+ return digest();
+ }
+
+ public abstract T digest() throws Exception;
+ public abstract T digest( byte [] bytes) throws Exception;
+ public abstract String getAlgorithm();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Key.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Key.java
new file mode 100644
index 0000000..160ec05
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Key.java
@@ -0,0 +1,8 @@
+package aQute.libg.cryptography;
+
+
+public abstract class Key implements java.security.Key {
+ private static final long serialVersionUID = 1L;
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java b/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java
new file mode 100644
index 0000000..3fd7158
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java
@@ -0,0 +1,33 @@
+package aQute.libg.cryptography;
+
+import java.security.*;
+
+
+
+public class MD5 extends Digest {
+ public final static String ALGORITHM = "MD5";
+
+ public static Digester<MD5> getDigester() throws Exception {
+ return new Digester<MD5>(MessageDigest.getInstance(ALGORITHM)) {
+
+ @Override public MD5 digest() throws Exception {
+ return new MD5(md.digest());
+ }
+
+ @Override public MD5 digest(byte[] bytes) {
+ return new MD5(bytes);
+ }
+ @Override public String getAlgorithm() {
+ return ALGORITHM;
+ }
+ };
+ }
+
+
+ public MD5(byte[] digest) {
+ super(digest,16);
+ }
+
+ @Override public String getAlgorithm() { return ALGORITHM; }
+
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/RSA.java b/bundleplugin/src/main/java/aQute/libg/cryptography/RSA.java
new file mode 100644
index 0000000..61bafb5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/RSA.java
@@ -0,0 +1,43 @@
+package aQute.libg.cryptography;
+
+import java.math.*;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+
+import aQute.libg.tuple.*;
+
+public class RSA {
+ final static String ALGORITHM = "RSA";
+
+ final static KeyFactory factory = getKeyFactory();
+
+ static private KeyFactory getKeyFactory() {
+ try {
+ return KeyFactory.getInstance(ALGORITHM);
+ } catch (Exception e) {
+ // built in
+ }
+ return null;
+ }
+
+ public static RSAPrivateKey create(RSAPrivateKeySpec keyspec) throws InvalidKeySpecException {
+ return (RSAPrivateKey) factory.generatePrivate(keyspec);
+ }
+ public static RSAPublicKey create(RSAPublicKeySpec keyspec) throws InvalidKeySpecException {
+ return (RSAPublicKey) factory.generatePrivate(keyspec);
+ }
+
+ public static RSAPublicKey createPublic(BigInteger m, BigInteger e) throws InvalidKeySpecException {
+ return create( new RSAPublicKeySpec(m,e));
+ }
+ public static RSAPrivateKey createPrivate(BigInteger m, BigInteger e) throws InvalidKeySpecException {
+ return create( new RSAPrivateKeySpec(m,e));
+ }
+
+ public static Pair<RSAPrivateKey, RSAPublicKey> generate() throws NoSuchAlgorithmException {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
+ KeyPair keypair = kpg.generateKeyPair();
+ return new Pair<RSAPrivateKey,RSAPublicKey>( (RSAPrivateKey) keypair.getPrivate(), (RSAPublicKey) keypair.getPublic());
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java b/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java
new file mode 100644
index 0000000..22e725b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java
@@ -0,0 +1,33 @@
+package aQute.libg.cryptography;
+
+import java.security.*;
+
+
+
+public class SHA1 extends Digest {
+ public final static String ALGORITHM = "SHA1";
+
+ public static Digester<SHA1> getDigester() throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance(ALGORITHM);
+ return new Digester<SHA1>(md) {
+ @Override public SHA1 digest() throws Exception {
+ return new SHA1(md.digest());
+ }
+
+ @Override public SHA1 digest(byte[] bytes) {
+ return new SHA1(bytes);
+ }
+ @Override public String getAlgorithm() {
+ return ALGORITHM;
+ }
+ };
+ }
+
+ public SHA1(byte[] b) {
+ super(b, 20);
+ }
+
+
+ @Override public String getAlgorithm() { return ALGORITHM; }
+
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Signer.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Signer.java
new file mode 100644
index 0000000..a068677
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Signer.java
@@ -0,0 +1,35 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+public class Signer<D extends Digest> extends OutputStream {
+ Signature signature;
+ Digester<D> digester;
+
+ Signer(Signature s, Digester<D> digester) {
+ this.signature = s;
+ this.digester = digester;
+ }
+
+ @Override public void write(byte[] buffer, int offset, int length) throws IOException {
+ try {
+ signature.update(buffer, offset, length);
+ } catch (SignatureException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ try {
+ signature.update((byte) b);
+ } catch (SignatureException e) {
+ throw new IOException(e);
+ }
+ }
+
+
+ public D signature() throws Exception {
+ return digester.digest(signature().digest());
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/Verifier.java b/bundleplugin/src/main/java/aQute/libg/cryptography/Verifier.java
new file mode 100644
index 0000000..90d6993
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Verifier.java
@@ -0,0 +1,38 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+
+public class Verifier extends OutputStream {
+ final Signature signature;
+ final Digest d;
+
+ Verifier(Signature s, Digest d) {
+ this.signature = s;
+ this.d = d;
+ }
+
+ @Override
+ public void write( byte[] buffer, int offset, int length) throws IOException {
+ try {
+ signature.update(buffer, offset, length);
+ } catch (SignatureException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void write( int b) throws IOException {
+ try {
+ signature.update((byte) b);
+ } catch (SignatureException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public boolean verify() throws Exception {
+ return signature.verify(d.digest());
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cryptography/packageinfo b/bundleplugin/src/main/java/aQute/libg/cryptography/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/fileiterator/FileIterator.java b/bundleplugin/src/main/java/aQute/libg/fileiterator/FileIterator.java
new file mode 100644
index 0000000..c60ed6b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/fileiterator/FileIterator.java
@@ -0,0 +1,45 @@
+package aQute.libg.fileiterator;
+
+import java.io.*;
+import java.util.*;
+
+public class FileIterator implements Iterator<File> {
+ File dir;
+ int n = 0;
+ FileIterator next;
+
+ public FileIterator(File nxt) {
+ assert nxt.isDirectory();
+ this.dir = nxt;
+ }
+
+ public boolean hasNext() {
+ if (next != null)
+ return next.hasNext();
+ else
+ return n < dir.list().length;
+ }
+
+ public File next() {
+ if (next != null) {
+ File answer = next.next();
+ if (!next.hasNext())
+ next = null;
+ return answer;
+ } else {
+ File nxt = dir.listFiles()[n++];
+ if (nxt.isDirectory()) {
+ next = new FileIterator(nxt);
+ return nxt;
+ } else if (nxt.isFile()) {
+ return nxt;
+ } else
+ throw new IllegalStateException("File disappeared");
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "Cannot remove from a file iterator");
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/fileiterator/packageinfo b/bundleplugin/src/main/java/aQute/libg/fileiterator/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/fileiterator/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/filelock/DirectoryLock.java b/bundleplugin/src/main/java/aQute/libg/filelock/DirectoryLock.java
new file mode 100644
index 0000000..6e6b11e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filelock/DirectoryLock.java
@@ -0,0 +1,32 @@
+package aQute.libg.filelock;
+
+import java.io.*;
+
+public class DirectoryLock {
+ final File lock;
+ final long timeout;
+ final public static String LOCKNAME = ".lock";
+
+ public DirectoryLock(File directory, long timeout) {
+ this.lock = new File(directory, LOCKNAME);
+ this.lock.deleteOnExit();
+ this.timeout = timeout;
+ }
+
+
+ public void release() {
+ lock.delete();
+ }
+
+ public void lock() throws InterruptedException {
+ if (lock.mkdir())
+ return;
+
+ long deadline = System.currentTimeMillis()+ timeout;
+ while ( System.currentTimeMillis() < deadline) {
+ if (lock.mkdir())
+ return;
+ Thread.sleep(50);
+ }
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/forker/Forker.java b/bundleplugin/src/main/java/aQute/libg/forker/Forker.java
new file mode 100644
index 0000000..530feb0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/forker/Forker.java
@@ -0,0 +1,184 @@
+package aQute.libg.forker;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+/**
+ * A Forker is good in parallel scheduling tasks with dependencies. You can add
+ * tasks with {@link #doWhen(Collection, Object, Runnable)}. The collection is
+ * the list of dependencies, the object is the target, and the runnable is run
+ * to update the target. The runnable will only run when all its dependencies
+ * have ran their associated runnable.
+ *
+ * @author aqute
+ *
+ * @param <T>
+ */
+public class Forker<T> {
+ final Executor executor;
+ final Set<T> done = new HashSet<T>();
+ final List<Job> waiting = new ArrayList<Job>();
+ final Semaphore semaphore = new Semaphore(0);
+ final AtomicInteger outstanding = new AtomicInteger();
+ final AtomicBoolean canceled = new AtomicBoolean();
+
+ /**
+ * Helper class to model a Job
+ */
+ class Job implements Runnable {
+ T target;
+ Set<T> dependencies;
+ Runnable runnable;
+ Throwable exception;
+ volatile Thread t;
+ volatile AtomicBoolean canceled = new AtomicBoolean(false);
+
+ /**
+ * Run when the job's dependencies are done.
+ */
+ public void run() {
+ Thread.interrupted(); // clear the interrupt flag
+
+ try {
+ synchronized (this) {
+ // Check if we got canceled
+ if (canceled.get())
+ return;
+
+ t = Thread.currentThread();
+ }
+ runnable.run();
+ } catch (Exception e) {
+ exception = e;
+ } finally {
+ synchronized (this) {
+ t = null;
+ }
+ Thread.interrupted(); // clear the interrupt flag
+ done(target);
+ }
+ }
+
+ /**
+ * Cancel this job
+ */
+ private void cancel() {
+ if (!canceled.getAndSet(true)) {
+ synchronized (this) {
+ if (t != null)
+ t.interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param executor
+ */
+ public Forker(Executor executor) {
+ this.executor = executor;
+ }
+
+ /**
+ * Constructor
+ *
+ */
+ public Forker() {
+ this.executor = Executors.newFixedThreadPool(4);
+ }
+
+ /**
+ * Schedule a job for execution when the dependencies are done of target are
+ * done.
+ *
+ * @param dependencies the dependencies that must have run
+ * @param target the target, is removed from all the dependencies when it ran
+ * @param runnable the runnable to run
+ */
+ public synchronized void doWhen(Collection<? extends T> dependencies, T target,
+ Runnable runnable) {
+ System.out.println("doWhen " + dependencies);
+ outstanding.incrementAndGet();
+ Job job = new Job();
+ job.dependencies = new HashSet<T>(dependencies);
+ job.dependencies.removeAll(done);
+ job.target = target;
+
+ job.runnable = runnable;
+ if (job.dependencies.isEmpty()) {
+ executor.execute(job);
+ } else {
+ waiting.add(job);
+ }
+ }
+
+ /**
+ * Called when the target has ran by the Job.
+ *
+ * @param done
+ */
+ private void done(T done) {
+ List<Runnable> torun = new ArrayList<Runnable>();
+ synchronized (this) {
+ System.out.println("done " + done);
+ semaphore.release();
+
+ for (Iterator<Job> e = waiting.iterator(); e.hasNext();) {
+ Job job = e.next();
+ if (job.dependencies.remove(done) && job.dependencies.isEmpty()) {
+ System.out.println("scheduling " + job.target);
+ torun.add(job);
+ e.remove();
+ }
+ }
+ }
+ for (Runnable r : torun)
+ executor.execute(r);
+ }
+
+ /**
+ * Wait until all jobs have run.
+ *
+ * @throws InterruptedException
+ */
+ public void join() throws InterruptedException {
+ System.out.println("join " + outstanding + " " + semaphore);
+ check();
+ semaphore.acquire(outstanding.getAndSet(0));
+ }
+
+ /**
+ * Check that we have no jobs that can never be satisfied. I.e. if
+ * the dependencies contain a target that is not listed.
+ */
+ private void check() {
+ // TODO
+ }
+
+ /**
+ * Return the number of outstanding jobs
+ * @return outstanding jobs
+ */
+ public int getOutstanding() {
+ return semaphore.availablePermits();
+ }
+
+ /**
+ * Cancel the forker.
+ *
+ * @throws InterruptedException
+ */
+ public void cancel() throws InterruptedException {
+ System.out.println("canceled " + outstanding + " " + semaphore);
+
+ if (!canceled.getAndSet(true)) {
+ for (Job job : waiting) {
+ job.cancel();
+ }
+ }
+ join();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/forker/packageinfo b/bundleplugin/src/main/java/aQute/libg/forker/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/forker/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/generics/Create.java b/bundleplugin/src/main/java/aQute/libg/generics/Create.java
new file mode 100644
index 0000000..6edc687
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/generics/Create.java
@@ -0,0 +1,42 @@
+package aQute.libg.generics;
+
+import java.util.*;
+
+public class Create {
+
+ public static <K,V> Map<K, V> map() {
+ return new LinkedHashMap<K,V>();
+ }
+
+ public static <T> List<T> list() {
+ return new ArrayList<T>();
+ }
+
+ public static <T> Set<T> set() {
+ return new HashSet<T>();
+ }
+
+ public static <T> List<T> list(T[] source) {
+ return new ArrayList<T>(Arrays.asList(source));
+ }
+
+ public static <T> Set<T> set(T[]source) {
+ return new HashSet<T>(Arrays.asList(source));
+ }
+
+ public static <K,V> Map<K, V> copy(Map<K,V> source) {
+ return new LinkedHashMap<K,V>(source);
+ }
+
+ public static <T> List<T> copy(List<T> source) {
+ return new ArrayList<T>(source);
+ }
+
+ public static <T> Set<T> copy(Collection<T> source) {
+ if ( source == null )
+ return set();
+ return new HashSet<T>(source);
+ }
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/generics/packageinfo b/bundleplugin/src/main/java/aQute/libg/generics/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/generics/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
new file mode 100644
index 0000000..5632034
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
@@ -0,0 +1,145 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+import aQute.libg.qtokens.*;
+import aQute.libg.reporter.*;
+
+public class OSGiHeader {
+
+ static public Map<String, Map<String, String>> parseHeader(String value) {
+ return parseHeader(value, null);
+ }
+
+ /**
+ * Standard OSGi header parser. This parser can handle the format clauses
+ * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
+ * value )
+ *
+ * This is mapped to a Map { name => Map { attr|directive => value } }
+ *
+ * @param value
+ * A string
+ * @return a Map<String,Map<String,String>>
+ */
+ static public Map<String, Map<String, String>> parseHeader(String value,
+ Reporter logger) {
+ if (value == null || value.trim().length() == 0)
+ return Create.map();
+
+ Map<String, Map<String, String>> result = Create.map();
+ QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+ char del = 0;
+ do {
+ boolean hadAttribute = false;
+ Map<String, String> clause = Create.map();
+ List<String> aliases = Create.list();
+ String name = qt.nextToken(",;");
+
+ del = qt.getSeparator();
+ if (name == null || name.length() == 0) {
+ if (logger != null && logger.isPedantic()) {
+ logger
+ .warning("Empty clause, usually caused by repeating a comma without any name field or by having spaces after the backslash of a property file: "
+ + value);
+ }
+ if (name == null)
+ break;
+ } else {
+ name = name.trim();
+
+ aliases.add(name);
+ while (del == ';') {
+ String adname = qt.nextToken();
+ if ((del = qt.getSeparator()) != '=') {
+ if (hadAttribute)
+ if (logger != null) {
+ logger
+ .error("Header contains name field after attribute or directive: "
+ + adname
+ + " from "
+ + value
+ + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
+ }
+ if (adname != null && adname.length() > 0)
+ aliases.add(adname.trim());
+ } else {
+ String advalue = qt.nextToken();
+ if (clause.containsKey(adname)) {
+ if (logger != null && logger.isPedantic())
+ logger
+ .warning("Duplicate attribute/directive name "
+ + adname
+ + " in "
+ + value
+ + ". This attribute/directive will be ignored");
+ }
+ if (advalue == null) {
+ if (logger != null)
+ logger
+ .error("No value after '=' sign for attribute "
+ + adname);
+ advalue = "";
+ }
+ clause.put(adname.trim(), advalue.trim());
+ del = qt.getSeparator();
+ hadAttribute = true;
+ }
+ }
+
+ // Check for duplicate names. The aliases list contains
+ // the list of nams, for each check if it exists. If so,
+ // add a number of "~" to make it unique.
+ for (String clauseName : aliases) {
+ if (result.containsKey(clauseName)) {
+ if (logger != null && logger.isPedantic())
+ logger
+ .warning("Duplicate name "
+ + clauseName
+ + " used in header: '"
+ + clauseName
+ + "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
+ while (result.containsKey(clauseName))
+ clauseName += "~";
+ }
+ result.put(clauseName, clause);
+ }
+ }
+ } while (del == ',');
+ return result;
+ }
+
+ public static Map<String, String> parseProperties(String input) {
+ return parseProperties(input, null);
+ }
+
+ public static Map<String, String> parseProperties(String input, Reporter logger) {
+ if (input == null || input.trim().length() == 0)
+ return Create.map();
+
+ Map<String, String> result = Create.map();
+ QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
+ char del = ',';
+
+ while (del == ',') {
+ String key = qt.nextToken(",=");
+ String value = "";
+ del = qt.getSeparator();
+ if (del == '=') {
+ value = qt.nextToken(",=");
+ del = qt.getSeparator();
+ }
+ result.put(key, value);
+ }
+ if (del != 0)
+ if ( logger == null )
+ throw new IllegalArgumentException(
+ "Invalid syntax for properties: " + input);
+ else
+ logger.error("Invalid syntax for properties: " + input);
+
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/header/packageinfo b/bundleplugin/src/main/java/aQute/libg/header/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java b/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
new file mode 100644
index 0000000..43ef7c4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
@@ -0,0 +1,118 @@
+package aQute.libg.qtokens;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+
+public class QuotedTokenizer {
+ String string;
+ int index = 0;
+ String separators;
+ boolean returnTokens;
+ boolean ignoreWhiteSpace = true;
+ String peek;
+ char separator;
+
+ public QuotedTokenizer(String string, String separators, boolean returnTokens ) {
+ if ( string == null )
+ throw new IllegalArgumentException("string argument must be not null");
+ this.string = string;
+ this.separators = separators;
+ this.returnTokens = returnTokens;
+ }
+ public QuotedTokenizer(String string, String separators) {
+ this(string,separators,false);
+ }
+
+ public String nextToken(String separators) {
+ separator = 0;
+ if ( peek != null ) {
+ String tmp = peek;
+ peek = null;
+ return tmp;
+ }
+
+ if ( index == string.length())
+ return null;
+
+ StringBuffer sb = new StringBuffer();
+
+ while (index < string.length()) {
+ char c = string.charAt(index++);
+
+ if ( Character.isWhitespace(c)) {
+ if ( index == string.length())
+ break;
+ else {
+ sb.append(c);
+ continue;
+ }
+ }
+
+ if (separators.indexOf(c) >= 0) {
+ if (returnTokens)
+ peek = Character.toString(c);
+ else
+ separator = c;
+ break;
+ }
+
+ switch (c) {
+ case '"' :
+ case '\'' :
+ quotedString(sb, c);
+ break;
+
+ default :
+ sb.append(c);
+ }
+ }
+ String result = sb.toString().trim();
+ if ( result.length()==0 && index==string.length())
+ return null;
+ return result;
+ }
+
+ public String nextToken() {
+ return nextToken(separators);
+ }
+
+ private void quotedString(StringBuffer sb, char c) {
+ char quote = c;
+ while (index < string.length()) {
+ c = string.charAt(index++);
+ if (c == quote)
+ break;
+ if (c == '\\' && index < string.length()
+ && string.charAt(index + 1) == quote)
+ c = string.charAt(index++);
+ sb.append(c);
+ }
+ }
+
+ public String[] getTokens() {
+ return getTokens(0);
+ }
+
+ private String [] getTokens(int cnt){
+ String token = nextToken();
+ if ( token == null )
+ return new String[cnt];
+
+ String result[] = getTokens(cnt+1);
+ result[cnt]=token;
+ return result;
+ }
+
+ public char getSeparator() { return separator; }
+
+ public List<String> getTokenSet() {
+ List<String> list = Create.list();
+ String token = nextToken();
+ while ( token != null ) {
+ list.add(token);
+ token = nextToken();
+ }
+ return list;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/qtokens/packageinfo b/bundleplugin/src/main/java/aQute/libg/qtokens/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/qtokens/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java b/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java
new file mode 100644
index 0000000..c6179af
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java
@@ -0,0 +1,15 @@
+package aQute.libg.reporter;
+
+import java.util.*;
+
+
+public interface Reporter {
+ void error(String s, Object ... args);
+ void warning(String s, Object ... args);
+ void progress(String s, Object ... args);
+ void trace(String s, Object ... args);
+ List<String> getWarnings();
+ List<String> getErrors();
+
+ boolean isPedantic();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/packageinfo b/bundleplugin/src/main/java/aQute/libg/reporter/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/ContentFilter.java b/bundleplugin/src/main/java/aQute/libg/sax/ContentFilter.java
new file mode 100644
index 0000000..62ca259
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/ContentFilter.java
@@ -0,0 +1,8 @@
+package aQute.libg.sax;
+
+import org.xml.sax.ContentHandler;
+
+public interface ContentFilter extends ContentHandler {
+ void setParent(ContentHandler parent);
+ ContentHandler getParent();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/ContentFilterImpl.java b/bundleplugin/src/main/java/aQute/libg/sax/ContentFilterImpl.java
new file mode 100644
index 0000000..7f71568
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/ContentFilterImpl.java
@@ -0,0 +1,71 @@
+package aQute.libg.sax;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+public class ContentFilterImpl implements ContentFilter {
+
+ private ContentHandler parent;
+
+ public void setParent(ContentHandler parent) {
+ this.parent = parent;
+
+ }
+
+ public ContentHandler getParent() {
+ return parent;
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ parent.setDocumentLocator(locator);
+ }
+
+ public void startDocument() throws SAXException {
+ parent.startDocument();
+ }
+
+ public void endDocument() throws SAXException {
+ parent.endDocument();
+ }
+
+ public void startPrefixMapping(String prefix, String uri)
+ throws SAXException {
+ parent.startPrefixMapping(prefix, uri);
+ }
+
+ public void endPrefixMapping(String prefix) throws SAXException {
+ parent.endPrefixMapping(prefix);
+ }
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes atts) throws SAXException {
+ parent.startElement(uri, localName, qName, atts);
+ }
+
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ parent.endElement(uri, localName, qName);
+ }
+
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+ parent.characters(ch, start, length);
+ }
+
+ public void ignorableWhitespace(char[] ch, int start, int length)
+ throws SAXException {
+ parent.ignorableWhitespace(ch, start, length);
+ }
+
+ public void processingInstruction(String target, String data)
+ throws SAXException {
+ parent.processingInstruction(target, data);
+ }
+
+ public void skippedEntity(String name) throws SAXException {
+ parent.skippedEntity(name);
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/SAXElement.java b/bundleplugin/src/main/java/aQute/libg/sax/SAXElement.java
new file mode 100644
index 0000000..b7ce35e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/SAXElement.java
@@ -0,0 +1,36 @@
+package aQute.libg.sax;
+
+import org.xml.sax.Attributes;
+
+public class SAXElement {
+
+ private final String uri;
+ private final String localName;
+ private final String qName;
+ private final Attributes atts;
+
+ public SAXElement(String uri, String localName, String qName,
+ Attributes atts) {
+ this.uri = uri;
+ this.localName = localName;
+ this.qName = qName;
+ this.atts = atts;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public String getLocalName() {
+ return localName;
+ }
+
+ public String getqName() {
+ return qName;
+ }
+
+ public Attributes getAtts() {
+ return atts;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/SAXUtil.java b/bundleplugin/src/main/java/aQute/libg/sax/SAXUtil.java
new file mode 100644
index 0000000..87b058e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/SAXUtil.java
@@ -0,0 +1,29 @@
+package aQute.libg.sax;
+
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Result;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.XMLReader;
+
+public class SAXUtil {
+
+ public static XMLReader buildPipeline(Result output, ContentFilter... filters) throws Exception {
+ SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ TransformerHandler handler = factory.newTransformerHandler();
+ handler.setResult(output);
+
+ ContentHandler last = handler;
+ if (filters != null) for (ContentFilter filter : filters) {
+ filter.setParent(last);
+ last = filter;
+ }
+ XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+ reader.setContentHandler(last);
+
+ return reader;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/filters/ElementSelectionFilter.java b/bundleplugin/src/main/java/aQute/libg/sax/filters/ElementSelectionFilter.java
new file mode 100644
index 0000000..4411db9
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/filters/ElementSelectionFilter.java
@@ -0,0 +1,49 @@
+package aQute.libg.sax.filters;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import aQute.libg.sax.ContentFilterImpl;
+
+public abstract class ElementSelectionFilter extends ContentFilterImpl{
+
+ int depth = 0;
+ int hiddenDepth = -1;
+
+ protected abstract boolean select(int depth, String uri, String localName, String qName, Attributes attribs);
+
+ @Override
+ public final void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+ if (hiddenDepth < 0) {
+ boolean allow = select(depth, uri, localName, qName, atts);
+ if (allow)
+ super.startElement(uri, localName, qName, atts);
+ else
+ hiddenDepth = 0;
+ } else {
+ hiddenDepth ++;
+ }
+ depth++;
+ }
+
+ @Override
+ public final void endElement(String uri, String localName, String qName) throws SAXException {
+ if (hiddenDepth < 0) {
+ super.endElement(uri, localName, qName);
+ } else {
+ hiddenDepth --;
+ }
+ depth --;
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (hiddenDepth < 0) super.characters(ch, start, length);
+ }
+
+ @Override
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ if (hiddenDepth < 0) super.ignorableWhitespace(ch, start, length);
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/filters/MergeContentFilter.java b/bundleplugin/src/main/java/aQute/libg/sax/filters/MergeContentFilter.java
new file mode 100644
index 0000000..acf5d12
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/filters/MergeContentFilter.java
@@ -0,0 +1,57 @@
+package aQute.libg.sax.filters;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import aQute.libg.sax.ContentFilterImpl;
+import aQute.libg.sax.SAXElement;
+
+public class MergeContentFilter extends ContentFilterImpl {
+
+ private int elementDepth = 0;
+
+ private final List<SAXElement> rootElements = new LinkedList<SAXElement>();
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+ if (elementDepth++ == 0) {
+ if (rootElements.isEmpty())
+ super.startElement(uri, localName, qName, atts);
+ else if (!rootElements.get(0).getqName().equals(qName))
+ throw new SAXException(String.format("Documents have inconsistent root element names: first was %s, current is %s.", rootElements.get(0).getqName(), qName));
+ rootElements.add(new SAXElement(uri, localName, qName, atts));
+ } else {
+ super.startElement(uri, localName, qName, atts);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (--elementDepth > 0) {
+ super.endElement(uri, localName, qName);
+ }
+ }
+
+ @Override
+ public void processingInstruction(String target, String data) throws SAXException {
+ if (rootElements.isEmpty())
+ super.processingInstruction(target, data);
+ }
+
+ public void closeRootAndDocument() throws SAXException {
+ if (!rootElements.isEmpty()) {
+ SAXElement root = rootElements.get(0);
+ super.endElement(root.getUri(), root.getLocalName(), root.getqName());
+ }
+ super.endDocument();
+ }
+
+ public List<SAXElement> getRootElements() {
+ return Collections.unmodifiableList(rootElements);
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/filters/packageinfo b/bundleplugin/src/main/java/aQute/libg/sax/filters/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/filters/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/sax/packageinfo b/bundleplugin/src/main/java/aQute/libg/sax/packageinfo
new file mode 100644
index 0000000..a4f1546
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sax/packageinfo
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/Replacer.java b/bundleplugin/src/main/java/aQute/libg/sed/Replacer.java
new file mode 100644
index 0000000..fa181f4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/Replacer.java
@@ -0,0 +1,5 @@
+package aQute.libg.sed;
+
+public interface Replacer {
+ String process(String line);
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/Sed.java b/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
new file mode 100644
index 0000000..86971bb
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
@@ -0,0 +1,82 @@
+package aQute.libg.sed;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+public class Sed {
+ final File file;
+ final Replacer macro;
+ File output;
+
+ final Map<Pattern, String> replacements = new LinkedHashMap<Pattern, String>();
+
+ public Sed(Replacer macro, File file) {
+ assert file.isFile();
+ this.file = file;
+ this.macro = macro;
+ }
+
+ public void setOutput(File f) {
+ output = f;
+ }
+
+ public void replace(String pattern, String replacement) {
+ replacements.put(Pattern.compile(pattern), replacement);
+ }
+
+ public void doIt() throws IOException {
+ BufferedReader brdr = new BufferedReader(new FileReader(file));
+ File out;
+ if (output != null)
+ out = output;
+ else
+ out = new File(file.getAbsolutePath() + ".tmp");
+ File bak = new File(file.getAbsolutePath() + ".bak");
+ PrintWriter pw = new PrintWriter(new FileWriter(out));
+ try {
+ String line;
+ while ((line = brdr.readLine()) != null) {
+ for (Pattern p : replacements.keySet()) {
+ String replace = replacements.get(p);
+ Matcher m = p.matcher(line);
+
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String tmp = setReferences(m, replace);
+ tmp = macro.process(tmp);
+ m.appendReplacement(sb, Matcher.quoteReplacement(tmp));
+ }
+ m.appendTail(sb);
+
+ line = sb.toString();
+ }
+ pw.println(line);
+ }
+ pw.close();
+ if (output == null) {
+ file.renameTo(bak);
+ out.renameTo(file);
+ }
+ } finally {
+ brdr.close();
+ pw.close();
+ }
+ }
+
+ private String setReferences(Matcher m, String replace) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < replace.length(); i++) {
+ char c = replace.charAt(i);
+ if (c == '$' && i < replace.length() - 1
+ && Character.isDigit(replace.charAt(i + 1))) {
+ int n = replace.charAt(i + 1) - '0';
+ if ( n <= m.groupCount() )
+ sb.append(m.group(n));
+ i++;
+ } else
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/packageinfo b/bundleplugin/src/main/java/aQute/libg/sed/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/tarjan/Tarjan.java b/bundleplugin/src/main/java/aQute/libg/tarjan/Tarjan.java
new file mode 100644
index 0000000..063006e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/tarjan/Tarjan.java
@@ -0,0 +1,108 @@
+package aQute.libg.tarjan;
+
+import static java.lang.Math.*;
+
+import java.util.*;
+
+public class Tarjan<T> {
+
+ public class Node {
+ final T name;
+ final List<Node> adjacent = new ArrayList<Node>();
+ int low = -1;
+ int index = -1;
+
+ public Node(T name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name + "{" + index + "," + low + "}";
+ }
+ }
+
+ private int index = 0;
+ private List<Node> stack = new ArrayList<Node>();
+ private Set<Set<T>> scc = new HashSet<Set<T>>();
+ private Node root = new Node(null);
+
+
+// public ArrayList<ArrayList<Node>> tarjan(Node v, AdjacencyList list){
+// v.index = index;
+// v.lowlink = index;
+// index++;
+// stack.add(0, v);
+// for(Edge e : list.getAdjacent(v)){
+// Node n = e.to;
+// if(n.index == -1){
+// tarjan(n, list);
+// v.lowlink = Math.min(v.lowlink, n.lowlink);
+// }else if(stack.contains(n)){
+// v.lowlink = Math.min(v.lowlink, n.index);
+// }
+// }
+// if(v.lowlink == v.index){
+// Node n;
+// ArrayList<Node> component = new ArrayList<Node>();
+// do{
+// n = stack.remove(0);
+// component.add(n);
+// }while(n != v);
+// SCC.add(component);
+// }
+// return SCC;
+// }
+
+ void tarjan(Node v) {
+ v.index = index;
+ v.low = index;
+ index++;
+ stack.add(0, v);
+ for (Node n : v.adjacent) {
+ if (n.index == -1) {
+ // first time visit
+ tarjan(n);
+ v.low = min(v.low, n.low);
+ } else if (stack.contains(n)) {
+ v.low = min(v.low, n.index);
+ }
+ }
+
+ if (v!=root && v.low == v.index) {
+ Set<T> component = new HashSet<T>();
+ Node n;
+ do {
+ n = stack.remove(0);
+ component.add(n.name);
+ } while (n != v);
+ scc.add(component);
+ }
+ }
+
+ Set<Set<T>> getResult(Map<T, ? extends Collection<T>> graph) {
+ Map<T, Node> index = new HashMap<T, Node>();
+
+ for (Map.Entry<T, ? extends Collection<T>> entry : graph.entrySet()) {
+ Node node = getNode(index, entry.getKey());
+ root.adjacent.add(node);
+ for (T adj : entry.getValue())
+ node.adjacent.add(getNode(index, adj));
+ }
+ tarjan(root);
+ return scc;
+ }
+
+ private Node getNode(Map<T, Node> index, T key) {
+ Node node = index.get(key);
+ if (node == null) {
+ node = new Node(key);
+ index.put(key, node);
+ }
+ return node;
+ }
+
+ public static <T> Set<Set<T>> tarjan(Map<T, Set<T>> graph) {
+ Tarjan<T> tarjan = new Tarjan<T>();
+ return tarjan.getResult(graph);
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/tarjan/packageinfo b/bundleplugin/src/main/java/aQute/libg/tarjan/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/tarjan/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
new file mode 100644
index 0000000..efb2298
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/tuple/Pair.java
@@ -0,0 +1,11 @@
+package aQute.libg.tuple;
+
+public class Pair<A,B> {
+ final public A a;
+ final public B b;
+
+ public Pair(A a, B b) {
+ this.a = a;
+ this.b = b;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/tuple/packageinfo b/bundleplugin/src/main/java/aQute/libg/tuple/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/tuple/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/version/Version.java b/bundleplugin/src/main/java/aQute/libg/version/Version.java
new file mode 100644
index 0000000..4f087a0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/Version.java
@@ -0,0 +1,148 @@
+package aQute.libg.version;
+
+import java.util.regex.*;
+
+public class Version implements Comparable<Version> {
+ final int major;
+ final int minor;
+ final int micro;
+ final String qualifier;
+ public final static String VERSION_STRING = "(\\d+)(\\.(\\d+)(\\.(\\d+)(\\.([-_\\da-zA-Z]+))?)?)?";
+ public final static Pattern VERSION = Pattern
+ .compile(VERSION_STRING);
+ public final static Version LOWEST = new Version();
+ public final static Version HIGHEST = new Version(Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ "\uFFFF");
+
+ public Version() {
+ this(0);
+ }
+
+ public Version(int major, int minor, int micro, String qualifier) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ this.qualifier = qualifier;
+ }
+
+ public Version(int major, int minor, int micro) {
+ this(major, minor, micro, null);
+ }
+
+ public Version(int major, int minor) {
+ this(major, minor, 0, null);
+ }
+
+ public Version(int major) {
+ this(major, 0, 0, null);
+ }
+
+ public Version(String version) {
+ Matcher m = VERSION.matcher(version);
+ if (!m.matches())
+ throw new IllegalArgumentException("Invalid syntax for version: "
+ + version);
+
+ major = Integer.parseInt(m.group(1));
+ if (m.group(3) != null)
+ minor = Integer.parseInt(m.group(3));
+ else
+ minor = 0;
+
+ if (m.group(5) != null)
+ micro = Integer.parseInt(m.group(5));
+ else
+ micro = 0;
+
+ qualifier = m.group(7);
+ }
+
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public int getMicro() {
+ return micro;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public int compareTo(Version other) {
+ if (other == this)
+ return 0;
+
+ if (!(other instanceof Version))
+ throw new IllegalArgumentException(
+ "Can only compare versions to versions");
+
+ Version o = (Version) other;
+ if (major != o.major)
+ return major - o.major;
+
+ if (minor != o.minor)
+ return minor - o.minor;
+
+ if (micro != o.micro)
+ return micro - o.micro;
+
+ int c = 0;
+ if (qualifier != null)
+ c = 1;
+ if (o.qualifier != null)
+ c += 2;
+
+ switch (c) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ return -1;
+ }
+ return qualifier.compareTo(o.qualifier);
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(major);
+ sb.append(".");
+ sb.append(minor);
+ sb.append(".");
+ sb.append(micro);
+ if (qualifier != null) {
+ sb.append(".");
+ sb.append(qualifier);
+ }
+ return sb.toString();
+ }
+
+ public boolean equals(Object ot) {
+ if ( ! (ot instanceof Version))
+ return false;
+
+ return compareTo((Version)ot) == 0;
+ }
+
+ public int hashCode() {
+ return major * 97 ^ minor * 13 ^ micro
+ + (qualifier == null ? 97 : qualifier.hashCode());
+ }
+
+ public int get(int i) {
+ switch(i) {
+ case 0 : return major;
+ case 1 : return minor;
+ case 2 : return micro;
+ default:
+ throw new IllegalArgumentException("Version can only get 0 (major), 1 (minor), or 2 (micro)");
+ }
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/version/VersionRange.java b/bundleplugin/src/main/java/aQute/libg/version/VersionRange.java
new file mode 100644
index 0000000..47e7447
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/VersionRange.java
@@ -0,0 +1,85 @@
+package aQute.libg.version;
+
+import java.util.regex.*;
+
+public class VersionRange {
+ Version high;
+ Version low;
+ char start = '[';
+ char end = ']';
+
+ static Pattern RANGE = Pattern.compile("(\\(|\\[)\\s*(" +
+ Version.VERSION_STRING + ")\\s*,\\s*(" +
+ Version.VERSION_STRING + ")\\s*(\\)|\\])");
+
+ public VersionRange(String string) {
+ string = string.trim();
+ Matcher m = RANGE.matcher(string);
+ if (m.matches()) {
+ start = m.group(1).charAt(0);
+ String v1 = m.group(2);
+ String v2 = m.group(10);
+ low = new Version(v1);
+ high = new Version(v2);
+ end = m.group(18).charAt(0);
+ if (low.compareTo(high) > 0)
+ throw new IllegalArgumentException(
+ "Low Range is higher than High Range: " + low + "-" +
+ high);
+
+ } else
+ high = low = new Version(string);
+ }
+
+ public boolean isRange() {
+ return high != low;
+ }
+
+ public boolean includeLow() {
+ return start == '[';
+ }
+
+ public boolean includeHigh() {
+ return end == ']';
+ }
+
+ public String toString() {
+ if (high == low)
+ return high.toString();
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(start);
+ sb.append(low);
+ sb.append(',');
+ sb.append(high);
+ sb.append(end);
+ return sb.toString();
+ }
+
+ public Version getLow() {
+ return low;
+ }
+
+ public Version getHigh() {
+ return high;
+ }
+
+ public boolean includes(Version v) {
+ if ( !isRange() ) {
+ return low.compareTo(v) <=0;
+ }
+ if (includeLow()) {
+ if (v.compareTo(low) < 0)
+ return false;
+ } else if (v.compareTo(low) <= 0)
+ return false;
+
+ if (includeHigh()) {
+ if (v.compareTo(high) > 0)
+ return false;
+ } else if (v.compareTo(high) >= 0)
+ return false;
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/version/packageinfo b/bundleplugin/src/main/java/aQute/libg/version/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/packageinfo
@@ -0,0 +1 @@
+version 1.0