Use local copy of latest bndlib code for pre-release testing purposes
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1347815 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/BER.java b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
new file mode 100644
index 0000000..4416dd4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/BER.java
@@ -0,0 +1,472 @@
+package aQute.libg.asn1;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+public class BER implements Types {
+ DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss\\Z");
+
+ final DataInputStream xin;
+ long position;
+
+ public BER(InputStream in) {
+ this.xin = new DataInputStream(in);
+ }
+
+ public void dump(PrintStream out) throws Exception {
+ int type = readByte();
+ long length = readLength();
+ if (type == -1 || length == -1)
+ throw new EOFException("Empty file");
+ dump(out, type, length, "");
+ }
+
+ void dump(PrintStream out, int type, long length, String indent)
+ throws Exception {
+ int clss = type >> 6;
+ int nmbr = type & 0x1F;
+ boolean cnst = (type & 0x20) != 0;
+
+ String tag = "[" + nmbr + "]";
+ if (clss == 0)
+ tag = TAGS[nmbr];
+
+ if (cnst) {
+ System.err.printf("%5d %s %s %s%n", length, indent, CLASSES[clss],
+ tag);
+ while (length > 1) {
+ long atStart = getPosition();
+ int t2 = read();
+ long l2 = readLength();
+ dump(out, t2, l2, indent + " ");
+ length -= getPosition() - atStart;
+ }
+ } else {
+ assert length < Integer.MAX_VALUE;
+ assert length >= 0;
+ byte[] data = new byte[(int) length];
+ readFully(data);
+ String summary;
+
+ switch (nmbr) {
+ case BOOLEAN:
+ assert length == 1;
+ summary = data[0] != 0 ? "true" : "false";
+ break;
+
+ case INTEGER:
+ long n = toLong(data);
+ summary = n + "";
+ break;
+
+ case UTF8_STRING:
+ case IA5STRING:
+ case VISIBLE_STRING:
+ case UNIVERSAL_STRING:
+ case PRINTABLE_STRING:
+ case UTCTIME:
+ summary = new String(data, "UTF-8");
+ break;
+
+ case OBJECT_IDENTIFIER:
+ summary = readOID(data);
+ break;
+
+ case GENERALIZED_TIME:
+ case GRAPHIC_STRING:
+ case GENERAL_STRING:
+ case CHARACTER_STRING:
+
+ case REAL:
+ case EOC:
+ case BIT_STRING:
+ case OCTET_STRING:
+ case NULL:
+ case OBJECT_DESCRIPTOR:
+ case EXTERNAL:
+ case ENUMERATED:
+ case EMBEDDED_PDV:
+ case RELATIVE_OID:
+ case NUMERIC_STRING:
+ case T61_STRING:
+ case VIDEOTEX_STRING:
+ case BMP_STRING:
+ default:
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 10 && i < data.length; i++) {
+ sb.append(Integer.toHexString(data[i]));
+ }
+ if (data.length > 10) {
+ sb.append("...");
+ }
+ summary = sb.toString();
+ break;
+ }
+ out.printf("%5d %s %s %s %s\n", length, indent, CLASSES[clss], tag,
+ summary);
+ }
+ }
+
+ long toLong(byte[] data) {
+ if (data[0] < 0) {
+ for (int i = 0; i < data.length; i++)
+ data[i] = (byte) (0xFF ^ data[i]);
+
+ return -(toLong(data) + 1);
+ }
+ long n = 0;
+ for (int i = 0; i < data.length; i++) {
+ n = n * 256 + data[i];
+ }
+ return n;
+ }
+
+ /**
+ * 8.1.3.3 For the definite form, the length octets shall consist of one or
+ * more octets, and shall represent the number of octets in the contents
+ * octets using either the short form (see 8.1.3.4) or the long form (see
+ * 8.1.3.5) as a sender's option. NOTE – The short form can only be used if
+ * the number of octets in the contents octets is less than or equal to 127.
+ * 8.1.3.4 In the short form, the length octets shall consist of a single
+ * octet in which bit 8 is zero and bits 7 to 1 encode the number of octets
+ * in the contents octets (which may be zero), as an unsigned binary integer
+ * with bit 7 as the most significant bit. EXAMPLE L = 38 can be encoded as
+ * 001001102 8.1.3.5 In the long form, the length octets shall consist of an
+ * initial octet and one or more subsequent octets. The initial octet shall
+ * be encoded as follows: a) bit 8 shall be one; b) bits 7 to 1 shall encode
+ * the number of subsequent octets in the length octets, as an unsigned
+ * binary integer with bit 7 as the most significant bit; c) the value
+ * 111111112 shall not be used. ISO/IEC 8825-1:2003 (E) NOTE 1 – This
+ * restriction is introduced for possible future extension. Bits 8 to 1 of
+ * the first subsequent octet, followed by bits 8 to 1 of the second
+ * subsequent octet, followed in turn by bits 8 to 1 of each further octet
+ * up to and including the last subsequent octet, shall be the encoding of
+ * an unsigned binary integer equal to the number of octets in the contents
+ * octets, with bit 8 of the first subsequent octet as the most significant
+ * bit. EXAMPLE L = 201 can be encoded as: 100000012 110010012 NOTE 2 – In
+ * the long form, it is a sender's option whether to use more length octets
+ * than the minimum necessary. 8.1.3.6 For the indefinite form, the length
+ * octets indicate that the contents octets are terminated by
+ * end-of-contents octets (see 8.1.5), and shall consist of a single octet.
+ * 8.1.3.6.1 The single octet shall have bit 8 set to one, and bits 7 to 1
+ * set to zero. 8.1.3.6.2 If this form of length is used, then
+ * end-of-contents octets (see 8.1.5) shall be present in the encoding
+ * following the contents octets. 8.1.4 Contents octets The contents octets
+ * shall consist of zero, one or more octets, and shall encode the data
+ * value as specified in subsequent clauses. NOTE – The contents octets
+ * depend on the type of the data value; subsequent clauses follow the same
+ * sequence as the definition of types in ASN.1. 8.1.5 End-of-contents
+ * octets The end-of-contents octets shall be present if the length is
+ * encoded as specified in 8.1.3.6, otherwise they shall not be present. The
+ * end-of-contents octets shall consist of two zero octets. NOTE – The
+ * end-of-contents octets can be considered as the encoding of a value whose
+ * tag is universal class, whose form is primitive, whose number of the tag
+ * is zero, and whose contents are absent, thus:
+ *
+ * End-of-contents Length Contents 0016 0016 Absent
+ *
+ * @return
+ */
+ private long readLength() throws IOException {
+ long n = readByte();
+ if (n > 0) {
+ // short form
+ return n;
+ }
+ // long form
+ int count = (int) (n & 0x7F);
+ if (count == 0) {
+ // indefinite form
+ return 0;
+ }
+ n = 0;
+ while (count-- > 0) {
+ n = n * 256 + read();
+ }
+ return n;
+ }
+
+ private int readByte() throws IOException {
+ position++;
+ return xin.readByte();
+ }
+
+ private void readFully(byte[] data) throws IOException {
+ position += data.length;
+ xin.readFully(data);
+ }
+
+ private long getPosition() {
+ return position;
+ }
+
+ private int read() throws IOException {
+ position++;
+ return xin.read();
+ }
+
+ String readOID(byte[] data) {
+ StringBuilder sb = new StringBuilder();
+ sb.append((0xFF & data[0]) / 40);
+ sb.append(".");
+ sb.append((0xFF & data[0]) % 40);
+
+ int i = 0;
+ while (++i < data.length) {
+ int n = 0;
+ while (data[i] < 0) {
+ n = n * 128 + (0x7F & data[i]);
+ i++;
+ }
+ n = n * 128 + data[i];
+ sb.append(".");
+ sb.append(n);
+ }
+
+ return sb.toString();
+ }
+
+ int getPayloadLength(PDU pdu) throws Exception {
+ switch (pdu.getTag() & 0x1F) {
+ case EOC:
+ return 1;
+
+ case BOOLEAN:
+ return 1;
+
+ case INTEGER:
+ return size(pdu.getInt());
+
+ case UTF8_STRING:
+ String s = pdu.getString();
+ byte[] encoded = s.getBytes("UTF-8");
+ return encoded.length;
+
+ case IA5STRING:
+ case VISIBLE_STRING:
+ case UNIVERSAL_STRING:
+ case PRINTABLE_STRING:
+ case GENERALIZED_TIME:
+ case GRAPHIC_STRING:
+ case GENERAL_STRING:
+ case CHARACTER_STRING:
+ case UTCTIME:
+ case NUMERIC_STRING: {
+ String str = pdu.getString();
+ encoded = str.getBytes("ASCII");
+ return encoded.length;
+ }
+
+ case OBJECT_IDENTIFIER:
+ case REAL:
+ case BIT_STRING:
+ return pdu.getBytes().length;
+
+ case OCTET_STRING:
+ case NULL:
+ case OBJECT_DESCRIPTOR:
+ case EXTERNAL:
+ case ENUMERATED:
+ case EMBEDDED_PDV:
+ case RELATIVE_OID:
+ case T61_STRING:
+ case VIDEOTEX_STRING:
+ case BMP_STRING:
+ return pdu.getBytes().length;
+
+ default:
+ throw new IllegalArgumentException("Invalid type: " + pdu);
+ }
+ }
+
+ int size(long value) {
+ if (value < 128)
+ return 1;
+
+ if (value <= 0xFF)
+ return 2;
+
+ if (value <= 0xFFFF)
+ return 3;
+
+ if (value <= 0xFFFFFF)
+ return 4;
+
+ if (value <= 0xFFFFFFFF)
+ return 5;
+
+ if (value <= 0xFFFFFFFFFFL)
+ return 6;
+
+ if (value <= 0xFFFFFFFFFFFFL)
+ return 7;
+
+ if (value <= 0xFFFFFFFFFFFFFFL)
+ return 8;
+
+ if (value <= 0xFFFFFFFFFFFFFFFFL)
+ return 9;
+
+ throw new IllegalArgumentException("length too long");
+ }
+
+ public void write(OutputStream out, PDU pdu) throws Exception {
+ byte id = 0;
+
+ switch (pdu.getClss()) {
+ case UNIVERSAL:
+ id |= 0;
+ break;
+ case APPLICATION:
+ id |= 0x40;
+ break;
+ case CONTEXT:
+ id |= 0x80;
+ break;
+ case PRIVATE:
+ id |= 0xC0;
+ break;
+ }
+
+ if (pdu.isConstructed())
+ id |= 0x20;
+
+ int tag = pdu.getTag();
+ if (tag >= 0 && tag < 31) {
+ id |= tag;
+ } else {
+ throw new UnsupportedOperationException("Cant do tags > 30");
+ }
+
+ out.write(id);
+
+ int length = getPayloadLength(pdu);
+ int size = size(length);
+ if (size == 1) {
+ out.write(length);
+ } else {
+ out.write(size);
+ while (--size >= 0) {
+ byte data = (byte) ((length >> (size * 8)) & 0xFF);
+ out.write(data);
+ }
+ }
+ writePayload(out, pdu);
+ }
+
+ void writePayload(OutputStream out, PDU pdu) throws Exception {
+ switch (pdu.getTag()) {
+ case EOC:
+ out.write(0);
+ break;
+
+ case BOOLEAN:
+ if (pdu.getBoolean())
+ out.write(-1);
+ else
+ out.write(0);
+ break;
+
+ case ENUMERATED:
+ case INTEGER: {
+ long value = pdu.getInt();
+ int size = size(value);
+ for (int i = size; i >= 0; i--) {
+ byte b = (byte) ((value >> (i * 8)) & 0xFF);
+ out.write(b);
+ }
+ }
+
+ case BIT_STRING: {
+ byte bytes[] = pdu.getBytes();
+ int unused = bytes[0];
+ assert unused <= 7;
+ int[] mask = { 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1 };
+ bytes[bytes.length - 1] &= (byte) mask[unused];
+ out.write(bytes);
+ break;
+ }
+
+ case RELATIVE_OID:
+ case OBJECT_IDENTIFIER: {
+ int[] oid = pdu.getOID();
+ assert oid.length > 2;
+ assert oid[0] < 4;
+ assert oid[1] < 40;
+ byte top = (byte) (oid[0] * 40 + oid[1]);
+ out.write(top);
+ for (int i = 2; i < oid.length; i++) {
+ putOid(out,oid[i]);
+ }
+ break;
+ }
+
+ case OCTET_STRING: {
+ byte bytes[] = pdu.getBytes();
+ out.write(bytes);
+ break;
+ }
+
+ case NULL:
+ break;
+
+ case BMP_STRING:
+ case GRAPHIC_STRING:
+ case VISIBLE_STRING:
+ case GENERAL_STRING:
+ case UNIVERSAL_STRING:
+ case CHARACTER_STRING:
+ case NUMERIC_STRING:
+ case PRINTABLE_STRING:
+ case VIDEOTEX_STRING:
+ case T61_STRING:
+ case REAL:
+ case EMBEDDED_PDV:
+ case EXTERNAL:
+ throw new UnsupportedEncodingException("dont know real, embedded PDV or external");
+
+ case UTF8_STRING: {
+ String s = pdu.getString();
+ byte [] data = s.getBytes("UTF-8");
+ out.write(data);
+ break;
+ }
+
+ case OBJECT_DESCRIPTOR:
+ case IA5STRING:
+ String s = pdu.getString();
+ byte [] data = s.getBytes("ASCII");
+ out.write(data);
+ break;
+
+
+ case SEQUENCE:
+ case SET: {
+ PDU pdus[] = pdu.getChildren();
+ for ( PDU p : pdus ) {
+ write(out, p);
+ }
+ }
+
+
+ case UTCTIME:
+ case GENERALIZED_TIME:
+ Date date = pdu.getDate();
+ String ss= df.format(date);
+ byte d[] = ss.getBytes("ASCII");
+ out.write(d);
+ break;
+
+ }
+ }
+
+
+ private void putOid(OutputStream out, int i) throws IOException {
+ if (i > 127) {
+ putOid(out, i >> 7);
+ out.write(0x80 + (i & 0x7F));
+ } else
+ out.write(i & 0x7F);
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/PDU.java b/bundleplugin/src/main/java/aQute/libg/asn1/PDU.java
new file mode 100644
index 0000000..033928c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/PDU.java
@@ -0,0 +1,114 @@
+package aQute.libg.asn1;
+
+import java.util.*;
+
+public class PDU implements Types, Iterable<PDU> {
+ final int identifier;
+ final Object payload;
+ byte data[] = new byte[100];
+
+
+ public PDU(int id, Object payload) {
+ identifier = id;
+ this.payload = payload;
+ }
+
+ public PDU(Date payload) {
+ identifier = UTCTIME;
+ this.payload = payload;
+ }
+
+ public PDU(int n) {
+ this(UNIVERSAL+INTEGER, n);
+ }
+
+ public PDU(boolean value) {
+ this(UNIVERSAL+BOOLEAN, value);
+ }
+
+ public PDU(String s) throws Exception {
+ this(UNIVERSAL+IA5STRING, s);
+ }
+
+ public PDU(byte[] data) {
+ this(UNIVERSAL+OCTET_STRING, data);
+ }
+
+ public PDU(BitSet bits) {
+ this(UNIVERSAL+BIT_STRING, bits);
+ }
+
+ public PDU(int top, int l1, int... remainder) {
+ identifier = UNIVERSAL+OBJECT_IDENTIFIER;
+ int[] ids = new int[remainder.length + 2];
+ ids[0] = top;
+ ids[1] = l1;
+ System.arraycopy(remainder, 0, ids, 2, remainder.length);
+ payload = ids;
+ }
+
+ public PDU(int tag, PDU... set) {
+ this(tag,(Object)set);
+ }
+
+ public PDU(PDU... set) {
+ this(SEQUENCE+CONSTRUCTED,set);
+ }
+
+
+ public int getTag() {
+ return identifier & TAGMASK;
+ }
+
+ int getClss() {
+ return identifier & CLASSMASK;
+ }
+
+ public boolean isConstructed() {
+ return (identifier & CONSTRUCTED) != 0;
+ }
+
+ public String getString() {
+ return (String) payload;
+ }
+
+ public Iterator<PDU> iterator() {
+ return Arrays.asList((PDU[]) payload).iterator();
+ }
+
+
+ public int[] getOID() {
+ assert getTag() == OBJECT_IDENTIFIER;
+ return (int[]) payload;
+ }
+
+ public Boolean getBoolean() {
+ assert getTag() == BOOLEAN;
+ return (Boolean) payload;
+ }
+
+ public BitSet getBits() {
+ assert getTag() == BIT_STRING;
+ return (BitSet) payload;
+ }
+
+ public int getInt() {
+ assert getTag() == INTEGER || getTag() == ENUMERATED;
+ return (Integer) payload;
+ }
+
+ public byte[] getBytes() {
+ return (byte[]) payload;
+ }
+
+ public PDU[] getChildren() {
+ assert isConstructed();
+ return (PDU[]) payload;
+ }
+
+ public Date getDate() {
+ assert getTag() == UTCTIME || getTag() == GENERALIZED_TIME;
+ return (Date) payload;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/Types.java b/bundleplugin/src/main/java/aQute/libg/asn1/Types.java
new file mode 100644
index 0000000..630ed1c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/Types.java
@@ -0,0 +1,65 @@
+package aQute.libg.asn1;
+
+public interface Types {
+ int UNIVERSAL = 0x00000000;
+ int APPLICATION = 0x40000000;
+ int CONTEXT = 0x80000000;
+ int PRIVATE = 0xC0000000;
+ int CLASSMASK = 0xC0000000;
+ int CONSTRUCTED = 0x20000000;
+ int TAGMASK = 0x1FFFFFFF;
+
+ String [] CLASSES = {"U", "A", "C", "P"};
+
+ // Payload Primitve
+ int EOC = 0; // null
+ // x
+ int BOOLEAN = 1; // Boolean
+ // x
+ int INTEGER = 2; // Long
+ // x
+ int BIT_STRING = 3; // byte
+ // [] -
+ int OCTET_STRING = 4; // byte
+ // [] -
+ int NULL = 5; // null
+ // x
+ int OBJECT_IDENTIFIER = 6; // int[]
+ // x
+ int OBJECT_DESCRIPTOR = 7; //
+ int EXTERNAL = 8; //
+ int REAL = 9; // double
+ // x
+ int ENUMERATED = 10; //
+ int EMBEDDED_PDV = 11; //
+ int UTF8_STRING = 12; // String
+ int RELATIVE_OID = 13; //
+ int SEQUENCE = 16; //
+ int SET = 17;
+ int NUMERIC_STRING = 18; // String
+ int PRINTABLE_STRING = 19; // String
+ int T61_STRING = 20; // String
+ int VIDEOTEX_STRING = 21; // String
+ int IA5STRING = 22; // String
+ int UTCTIME = 23; // Date
+ int GENERALIZED_TIME = 24; // Date
+ int GRAPHIC_STRING = 25; // String
+ int VISIBLE_STRING = 26; // String
+ int GENERAL_STRING = 27; // String
+ int UNIVERSAL_STRING = 28; // String
+ int CHARACTER_STRING = 29; // String
+ int BMP_STRING = 30; // byte[]
+
+ String[] TAGS = { "EOC ",
+ "BOOLEAN ", "INTEGER ", "BIT_STRING ",
+ "OCTET_STRING ", "NULL ", "OBJECT_IDENTIFIER ",
+ "OBJECT_DESCRIPTOR ", "EXTERNAL ", "REAL ",
+ "ENUMERATED ", "EMBEDDED_PDV ", "UTF8_STRING ",
+ "RELATIVE_OID ", "?(14) ", "?(15) ",
+ "SEQUENCE ", "SET ", "NUMERIC_STRING ",
+ "PRINTABLE_STRING ", "T61_STRING ", "VIDEOTEX_STRING ",
+ "IA5STRING ", "UTCTIME ", "GENERALIZED_TIME ",
+ "GRAPHIC_STRING ", "VISIBLE_STRING ", "GENERAL_STRING ",
+ "UNIVERSAL_STRING ", "CHARACTER_STRING ", "BMP_STRING ", };
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/algorithms b/bundleplugin/src/main/java/aQute/libg/asn1/algorithms
new file mode 100644
index 0000000..4fb6ab4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/algorithms
@@ -0,0 +1,22 @@
+---------------------------------------------------------------------------
+SignatureAlgorithm digestAlgo (I) signatureAlgo (RFC 2630)
+ (II) digestEncrAlgo(PKCS#7)
+---------------------------------------------------------------------------
+(A) sha1WithRSA 1.3.14.3.2.26 (Ia) 1.2.840.113549.1.1.5
+ (1.2.840.113549.1.1.5) (Ib) 1.2.840.113549.1.1.1
+ (II) 1.2.840.113549.1.1.1
+
+
+(B) md5WithRSA 1.2.840.1x9.2.5 (Ia) 1.2.840.113549.1.1.4
+ (1.2.840.113549.1.1.4) (Ib) 1.2.840.113549.1.1.1
+ (II) 1.2.840.113549.1.1.1
+
+
+(C) ripeMD160WithRsa 1.3.36.3.2.1 (Ia) 1.3.36.3.3.1.2
+ (1.3.36.3.3.1.2) (Ib) 1.2.840.113549.1.1.1
+ (II) 1.2.840.113549.1.1.1
+
+
+(D) sha1WithDsa 1.3.14.3.2.26 (Ia) 1.2.840.10040.4.3
+ (1.2.840.10040.4.3) (Ib) 1.2.840.10040.4.1 (?)
+ (II) 1.2.840.10040.4.1
diff --git a/bundleplugin/src/main/java/aQute/libg/asn1/packageinfo b/bundleplugin/src/main/java/aQute/libg/asn1/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/asn1/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java b/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java
new file mode 100644
index 0000000..0bb8d7b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cafs/CAFS.java
@@ -0,0 +1,414 @@
+package aQute.libg.cafs;
+
+import static aQute.lib.io.IO.*;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.security.*;
+import java.util.*;
+import java.util.concurrent.atomic.*;
+import java.util.zip.*;
+
+import aQute.lib.index.*;
+import aQute.libg.cryptography.*;
+
+/**
+ * CAFS implements a SHA-1 based file store. The basic idea is that every file
+ * in the universe has a unique SHA-1. Hard to believe but people smarter than
+ * me have come to that conclusion. This class maintains a compressed store of
+ * SHA-1 identified files. So if you have the SHA-1, you can get the contents.
+ *
+ * This makes it easy to store a SHA-1 instead of the whole file or maintain a
+ * naming scheme. An added advantage is that it is always easy to verify you get
+ * the right stuff. The SHA-1 Content Addressable File Store is the core
+ * underlying idea in Git.
+ */
+public class CAFS implements Closeable, Iterable<SHA1> {
+ final static byte[] CAFS = "CAFS".getBytes();
+ final static byte[] CAFE = "CAFE".getBytes();
+ final static String INDEXFILE = "index.idx";
+ final static String STOREFILE = "store.cafs";
+ final static String ALGORITHM = "SHA-1";
+ final static int KEYLENGTH = 20;
+ final static int HEADERLENGTH = 4 // CAFS
+ + 4 // flags
+ + 4 // compressed length
+ + 4 // uncompressed length
+ + KEYLENGTH // key
+ + 2 // header checksum
+ ;
+
+ final File home;
+ Index index;
+ RandomAccessFile store;
+ FileChannel channel;
+
+ /**
+ * Constructor for a Content Addressable File Store
+ *
+ * @param home
+ * @param create
+ * @throws Exception
+ */
+ public CAFS(File home, boolean create) throws Exception {
+ this.home = home;
+ if (!home.isDirectory()) {
+ if (create) {
+ home.mkdirs();
+ } else
+ throw new IllegalArgumentException("CAFS requires a directory with create=false");
+ }
+
+ index = new Index(new File(home, INDEXFILE), KEYLENGTH);
+ store = new RandomAccessFile(new File(home, STOREFILE), "rw");
+ channel = store.getChannel();
+ if (store.length() < 0x100) {
+ if (create) {
+ store.write(CAFS);
+ for (int i = 1; i < 64; i++)
+ store.writeInt(0);
+ channel.force(true);
+ } else
+ throw new IllegalArgumentException("Invalid store file, length is too short "
+ + store);
+ System.err.println(store.length());
+ }
+ store.seek(0);
+ if (!verifySignature(store, CAFS))
+ throw new IllegalArgumentException("Not a valid signature: CAFS at start of file");
+
+ }
+
+ /**
+ * Store an input stream in the CAFS while calculating and returning the
+ * SHA-1 code.
+ *
+ * @param in
+ * The input stream to store.
+ * @return The SHA-1 code.
+ * @throws Exception
+ * if anything goes wrong
+ */
+ public SHA1 write(InputStream in) throws Exception {
+
+ Deflater deflater = new Deflater();
+ MessageDigest md = MessageDigest.getInstance(ALGORITHM);
+ DigestInputStream din = new DigestInputStream(in, md);
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DeflaterOutputStream dout = new DeflaterOutputStream(bout, deflater);
+ copy(din, dout);
+
+ synchronized (store) {
+ // First check if it already exists
+ SHA1 sha1 = new SHA1(md.digest());
+
+ long search = index.search(sha1.digest());
+ if (search > 0)
+ return sha1;
+
+ byte[] compressed = bout.toByteArray();
+
+ // we need to append this file to our store,
+ // which requires a lock. However, we are in a race
+ // so others can get the lock between us getting
+ // the length and someone else getting the lock.
+ // So we must verify after we get the lock that the
+ // length was unchanged.
+ FileLock lock = null;
+ try {
+ long insertPoint;
+ int recordLength = compressed.length + HEADERLENGTH;
+
+ while (true) {
+ insertPoint = store.length();
+ lock = channel.lock(insertPoint, recordLength, false);
+
+ if (store.length() == insertPoint)
+ break;
+
+ // We got the wrong lock, someone else
+ // got in between reading the length
+ // and locking
+ lock.release();
+ }
+ int totalLength = deflater.getTotalIn();
+ store.seek(insertPoint);
+ update(sha1.digest(), compressed, totalLength);
+ index.insert(sha1.digest(), insertPoint);
+ return sha1;
+ } finally {
+ if (lock != null)
+ lock.release();
+ }
+ }
+ }
+
+ /**
+ * Read the contents of a sha 1 key.
+ *
+ * @param sha1
+ * The key
+ * @return An Input Stream on the content or null of key not found
+ * @throws Exception
+ */
+ public InputStream read(final SHA1 sha1) throws Exception {
+ synchronized (store) {
+ long offset = index.search(sha1.digest());
+ if (offset < 0)
+ return null;
+
+ byte[] readSha1;
+ byte[] buffer;
+ store.seek(offset);
+ if (!verifySignature(store, CAFE))
+ throw new IllegalArgumentException("No signature");
+
+ int flags =store.readInt();
+ int compressedLength = store.readInt();
+ int uncompressedLength = store.readInt();
+ readSha1 = new byte[KEYLENGTH];
+ store.read(readSha1);
+ SHA1 rsha1 = new SHA1(readSha1);
+
+ if (!sha1.equals(rsha1))
+ throw new IOException("SHA1 read and asked mismatch: " + sha1 + " " + rsha1);
+
+ short crc = store.readShort(); // Read CRC
+ if ( crc != checksum(flags,compressedLength, uncompressedLength, readSha1))
+ throw new IllegalArgumentException("Invalid header checksum: " + sha1);
+
+ buffer = new byte[compressedLength];
+ store.readFully(buffer);
+ return getSha1Stream(sha1, buffer, uncompressedLength);
+ }
+ }
+
+ public boolean exists(byte[] sha1) throws Exception {
+ return index.search(sha1) >= 0;
+ }
+
+ public void reindex() throws Exception {
+ long length;
+ synchronized (store) {
+ length = store.length();
+ if (length < 0x100)
+ throw new IllegalArgumentException(
+ "Store file is too small, need to be at least 256 bytes: " + store);
+ }
+
+ RandomAccessFile in = new RandomAccessFile(new File(home, STOREFILE), "r");
+ try {
+ byte[] signature = new byte[4];
+ in.readFully(signature);
+ if (!Arrays.equals(CAFS, signature))
+ throw new IllegalArgumentException("Store file does not start with CAFS: " + in);
+
+ in.seek(0x100);
+ File ixf = new File(home, "index.new");
+ Index index = new Index(ixf, KEYLENGTH);
+
+ while (in.getFilePointer() < length) {
+ long entry = in.getFilePointer();
+ SHA1 sha1 = verifyEntry(in);
+ index.insert(sha1.digest(), entry);
+ }
+
+ synchronized (store) {
+ index.close();
+ File indexFile = new File(home, INDEXFILE);
+ ixf.renameTo(indexFile);
+ this.index = new Index(indexFile, KEYLENGTH);
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ public void close() throws IOException {
+ synchronized (store) {
+ try {
+ store.close();
+ } finally {
+ index.close();
+ }
+ }
+ }
+
+ private SHA1 verifyEntry(RandomAccessFile in) throws IOException, NoSuchAlgorithmException {
+ byte[] signature = new byte[4];
+ in.readFully(signature);
+ if (!Arrays.equals(CAFE, signature))
+ throw new IllegalArgumentException("File is corrupted: " + in);
+
+ /* int flags = */in.readInt();
+ int compressedSize = in.readInt();
+ int uncompressedSize = in.readInt();
+ byte[] key = new byte[KEYLENGTH];
+ in.readFully(key);
+ SHA1 sha1 = new SHA1(key);
+
+ byte[] buffer = new byte[compressedSize];
+ in.readFully(buffer);
+
+ InputStream xin = getSha1Stream(sha1, buffer, uncompressedSize);
+ xin.skip(uncompressedSize);
+ xin.close();
+ return sha1;
+ }
+
+ private boolean verifySignature(DataInput din, byte[] org) throws IOException {
+ byte[] read = new byte[org.length];
+ din.readFully(read);
+ return Arrays.equals(read, org);
+ }
+
+ private InputStream getSha1Stream(final SHA1 sha1, byte[] buffer, final int total)
+ throws NoSuchAlgorithmException {
+ ByteArrayInputStream in = new ByteArrayInputStream(buffer);
+ InflaterInputStream iin = new InflaterInputStream(in) {
+ int count = 0;
+ final MessageDigest digestx = MessageDigest.getInstance(ALGORITHM);
+ final AtomicBoolean calculated = new AtomicBoolean();
+
+ @Override public int read(byte[] data, int offset, int length) throws IOException {
+ int size = super.read(data, offset, length);
+ if (size <= 0)
+ eof();
+ else {
+ count+=size;
+ this.digestx.update(data, offset, size);
+ }
+ return size;
+ }
+
+ public int read() throws IOException {
+ int c = super.read();
+ if (c < 0)
+ eof();
+ else {
+ count++;
+ this.digestx.update((byte) c);
+ }
+ return c;
+ }
+
+ void eof() throws IOException {
+ if ( calculated.getAndSet(true))
+ return;
+
+ if ( count != total )
+ throw new IOException("Counts do not match. Expected to read: " + total + " Actually read: " + count);
+
+ SHA1 calculatedSha1 = new SHA1(digestx.digest());
+ if (!sha1.equals(calculatedSha1))
+ throw ( new IOException("SHA1 caclulated and asked mismatch, asked: "
+ + sha1 + ", \nfound: " +calculatedSha1));
+ }
+
+ public void close() throws IOException {
+ eof();
+ super.close();
+ }
+ };
+ return iin;
+ }
+
+ /**
+ * Update a record in the store, assuming the store is at the right
+ * position.
+ *
+ * @param sha1
+ * The checksum
+ * @param compressed
+ * The compressed length
+ * @param totalLength
+ * The uncompressed length
+ * @throws IOException
+ * The exception
+ */
+ private void update(byte[] sha1, byte[] compressed, int totalLength) throws IOException {
+ //System.err.println("pos: " + store.getFilePointer());
+ store.write(CAFE); // 00-03 Signature
+ store.writeInt(0); // 04-07 Flags for the future
+ store.writeInt(compressed.length); // 08-11 Length deflated data
+ store.writeInt(totalLength); // 12-15 Length
+ store.write(sha1); // 16-35
+ store.writeShort( checksum(0,compressed.length, totalLength, sha1));
+ store.write(compressed);
+ channel.force(false);
+ }
+
+
+
+ private short checksum(int flags, int compressedLength, int totalLength, byte[] sha1) {
+ CRC32 crc = new CRC32();
+ crc.update(flags);
+ crc.update(flags>>8);
+ crc.update(flags>>16);
+ crc.update(flags>>24);
+ crc.update(compressedLength);
+ crc.update(compressedLength>>8);
+ crc.update(compressedLength>>16);
+ crc.update(compressedLength>>24);
+ crc.update(totalLength);
+ crc.update(totalLength>>8);
+ crc.update(totalLength>>16);
+ crc.update(totalLength>>24);
+ crc.update(sha1);
+ return (short) crc.getValue();
+ }
+
+ public Iterator<SHA1> iterator() {
+
+ return new Iterator<SHA1>() {
+ long position = 0x100;
+
+ public boolean hasNext() {
+ synchronized(store) {
+ try {
+ return position < store.length();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public SHA1 next() {
+ synchronized(store) {
+ try {
+ store.seek(position);
+ byte [] signature = new byte[4];
+ store.readFully(signature);
+ if ( !Arrays.equals(CAFE, signature))
+ throw new IllegalArgumentException("No signature");
+
+ int flags = store.readInt();
+ int compressedLength = store.readInt();
+ int totalLength = store.readInt();
+ byte []sha1 = new byte[KEYLENGTH];
+ store.readFully(sha1);
+ short crc = store.readShort();
+ if ( crc != checksum(flags,compressedLength, totalLength, sha1))
+ throw new IllegalArgumentException("Header checksum fails");
+
+ position += HEADERLENGTH + compressedLength;
+ return new SHA1(sha1);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Remvoe not supported, CAFS is write once");
+ }
+ };
+ }
+
+
+ public boolean isEmpty() throws IOException {
+ synchronized(store) {
+ return store.getFilePointer() <= 256;
+ }
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/cafs/packageinfo b/bundleplugin/src/main/java/aQute/libg/cafs/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cafs/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
new file mode 100755
index 0000000..e45cd39
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
@@ -0,0 +1,698 @@
+/* Copyright 2006 aQute SARL
+ * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
+package aQute.libg.classdump;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+public class ClassDumper {
+ /**
+ * <pre>
+ * ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its
+ * package.
+ * ACC_FINAL 0x0010 Declared final; no subclasses allowed.
+ * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the
+ * invokespecial instruction.
+ * ACC_INTERFACE 0x0200 Is an interface, not a
+ * class.
+ * ACC_ABSTRACT 0x0400 Declared abstract; may not be instantiated.
+ * </pre>
+ *
+ * @param mod
+ */
+ final static int ACC_PUBLIC = 0x0001; // Declared public; may be accessed
+ // from outside its package.
+ final static int ACC_FINAL = 0x0010; // Declared final; no subclasses
+ // allowed.
+ final static int ACC_SUPER = 0x0020; // Treat superclass methods
+ // specially when invoked by the
+ // invokespecial instruction.
+ final static int ACC_INTERFACE = 0x0200; // Is an interface, not a classs
+ final static int ACC_ABSTRACT = 0x0400; // Declared abstract; may not be
+ // instantiated.
+
+ final static class Assoc {
+ Assoc(byte tag, int a, int b) {
+ this.tag = tag;
+ this.a = a;
+ this.b = b;
+ }
+
+ byte tag;
+ int a;
+ int b;
+
+ }
+
+ final String path;
+ final static String NUM_COLUMN = "%-30s %d\n";
+ final static String HEX_COLUMN = "%-30s %x\n";
+ final static String STR_COLUMN = "%-30s %s\n";
+
+ PrintStream ps = System.err;
+ Object[] pool;
+ InputStream in;
+
+ public ClassDumper(String path) throws Exception {
+ this(path, new FileInputStream(new File(path)));
+ }
+
+ public ClassDumper(String path, InputStream in) throws IOException {
+ this.path = path;
+ this.in = in;
+ }
+
+ public void dump(PrintStream ps) throws Exception {
+ if (ps != null)
+ this.ps = ps;
+ DataInputStream din = new DataInputStream(in);
+ parseClassFile(din);
+ din.close();
+ }
+
+ void parseClassFile(DataInputStream in) throws IOException {
+ int magic = in.readInt();
+ if (magic != 0xCAFEBABE)
+ throw new IOException("Not a valid class file (no CAFEBABE header)");
+
+ ps.printf(HEX_COLUMN, "magic", magic);
+ int minor = in.readUnsignedShort(); // minor version
+ int major = in.readUnsignedShort(); // major version
+ ps.printf(STR_COLUMN, "version", "" + major + "." + minor);
+ int pool_size = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, "pool size", pool_size);
+ pool = new Object[pool_size];
+
+ process: for (int poolIndex = 1; poolIndex < pool_size; poolIndex++) {
+ byte tag = in.readByte();
+
+ switch (tag) {
+ case 0:
+ ps.printf("%30d tag (0)\n", poolIndex);
+ break process;
+
+ case 1:
+ String name = in.readUTF();
+ pool[poolIndex] = name;
+ ps.printf("%30d tag(1) utf8 '%s'\n", poolIndex, name);
+ break;
+
+ case 2:
+ throw new IOException("Invalid tag " + tag);
+
+ case 3:
+ int i = in.readInt();
+ pool[poolIndex] = Integer.valueOf(i);
+ ps.printf("%30d tag(3) int %s\n", poolIndex, i);
+ break;
+
+ case 4:
+ float f = in.readFloat();
+ pool[poolIndex] = new Float(f);
+ ps.printf("%30d tag(4) float %s\n", poolIndex, f);
+ break;
+
+ // For some insane optimization reason are
+ // the long and the double two entries in the
+ // constant pool. See 4.4.5
+ case 5:
+ long l = in.readLong();
+ pool[poolIndex] = Long.valueOf(l);
+ ps.printf("%30d tag(5) long %s\n", poolIndex, l);
+ poolIndex++;
+ break;
+
+ case 6:
+ double d = in.readDouble();
+ pool[poolIndex] = new Double(d);
+ ps.printf("%30d tag(6) double %s\n", poolIndex, d);
+ poolIndex++;
+ break;
+
+ case 7:
+ int class_index = in.readUnsignedShort();
+ pool[poolIndex] = Integer.valueOf(class_index);
+ ps.printf("%30d tag(7) constant classs %d\n", poolIndex,
+ class_index);
+ break;
+
+ case 8:
+ int string_index = in.readUnsignedShort();
+ pool[poolIndex] = Integer.valueOf(string_index);
+ ps.printf("%30d tag(8) constant string %d\n", poolIndex,
+ string_index);
+ break;
+
+ case 9: // Field ref
+ class_index = in.readUnsignedShort();
+ int name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc((byte) 9, class_index,
+ name_and_type_index);
+ ps.printf("%30d tag(9) field ref %d/%d\n", poolIndex,
+ class_index, name_and_type_index);
+ break;
+
+ case 10: // Method ref
+ class_index = in.readUnsignedShort();
+ name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc((byte) 10, class_index,
+ name_and_type_index);
+ ps.printf("%30d tag(10) method ref %d/%d\n", poolIndex,
+ class_index, name_and_type_index);
+ break;
+
+ case 11: // Interface and Method ref
+ class_index = in.readUnsignedShort();
+ name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc((byte) 11, class_index,
+ name_and_type_index);
+ ps.printf("%30d tag(11) interface and method ref %d/%d\n",
+ poolIndex, class_index, name_and_type_index);
+ break;
+
+ // Name and Type
+ case 12:
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
+ ps.printf("%30d tag(12) name and type %d/%d\n", poolIndex,
+ name_index, descriptor_index);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown tag: " + tag);
+ }
+ }
+
+ int access = in.readUnsignedShort(); // access
+ printAccess(access);
+ int this_class = in.readUnsignedShort();
+ int super_class = in.readUnsignedShort();
+ ps.printf("%-30s %x %s(#%d)\n", "this_class", access, pool[this_class],
+ this_class);
+ ps.printf("%-30s %s(#%d)\n", "super_class", pool[super_class],
+ super_class);
+
+ int interfaces_count = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, "interface count", interfaces_count);
+ for (int i = 0; i < interfaces_count; i++) {
+ int interface_index = in.readUnsignedShort();
+ ps.printf("%-30s interface %s(#%d)", "interface count",
+ pool[interface_index], interfaces_count);
+ }
+
+ int field_count = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, "field count", field_count);
+ for (int i = 0; i < field_count; i++) {
+ access = in.readUnsignedShort(); // access
+ printAccess(access);
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "field def", access,
+ pool[name_index], name_index, pool[descriptor_index],
+ descriptor_index);
+ doAttributes(in, " ");
+ }
+
+ int method_count = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, "method count", method_count);
+ for (int i = 0; i < method_count; i++) {
+ int access_flags = in.readUnsignedShort();
+ printAccess(access_flags);
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ ps.printf("%-30s %x %s(#%d) %s(#%d)\n", "method def", access_flags,
+ pool[name_index], name_index, pool[descriptor_index],
+ descriptor_index);
+ doAttributes(in, " ");
+ }
+
+ doAttributes(in, "");
+ if (in.read() >= 0)
+ ps.printf("Extra bytes follow ...");
+ }
+
+ /**
+ * Called for each attribute in the class, field, or method.
+ *
+ * @param in
+ * The stream
+ * @throws IOException
+ */
+ private void doAttributes(DataInputStream in, String indent)
+ throws IOException {
+ int attribute_count = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "attribute count", attribute_count);
+ for (int j = 0; j < attribute_count; j++) {
+ doAttribute(in, indent + j + ": ");
+ }
+ }
+
+ /**
+ * Process a single attribute, if not recognized, skip it.
+ *
+ * @param in
+ * the data stream
+ * @throws IOException
+ */
+ private void doAttribute(DataInputStream in, String indent)
+ throws IOException {
+ int attribute_name_index = in.readUnsignedShort();
+ long attribute_length = in.readInt();
+ attribute_length &= 0xFFFF;
+ String attributeName = (String) pool[attribute_name_index];
+ ps.printf("%-30s %s(#%d)\n", indent + "attribute", attributeName,
+ attribute_name_index);
+ if ("RuntimeVisibleAnnotations".equals(attributeName))
+ doAnnotations(in, indent);
+ else if ("SourceFile".equals(attributeName))
+ doSourceFile(in, indent);
+ else if ("Code".equals(attributeName))
+ doCode(in, indent);
+ else if ("LineNumberTable".equals(attributeName))
+ doLineNumberTable(in, indent);
+ else if ("LocalVariableTable".equals(attributeName))
+ doLocalVariableTable(in, indent);
+ else if ("InnerClasses".equals(attributeName))
+ doInnerClasses(in, indent);
+ else if ("Exceptions".equals(attributeName))
+ doExceptions(in, indent);
+ else if ("EnclosingMethod".equals(attributeName))
+ doEnclosingMethod(in, indent);
+ else if ("Signature".equals(attributeName))
+ doSignature(in, indent);
+ else if ("Synthetic".equals(attributeName))
+ ; // Is empty!
+ else if ("Deprecated".equals(attributeName))
+ ; // Is Empty
+ else {
+ ps.printf("%-30s %d\n", indent + "Unknown attribute, skipping",
+ attribute_length);
+ if (attribute_length > 0x7FFFFFFF) {
+ throw new IllegalArgumentException("Attribute > 2Gb");
+ }
+ byte buffer[] = new byte[(int) attribute_length];
+ in.readFully(buffer);
+ printHex(buffer);
+ }
+ }
+
+ /**
+ * <pre>
+ * Signature_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 signature_index;
+ * }
+ * </pre>
+ *
+ * @param in
+ * @param indent
+ */
+ void doSignature(DataInputStream in, String indent) throws IOException {
+ int signature_index = in.readUnsignedShort();
+ ps.printf("%-30s %s(#%d)\n", indent + "signature",
+ pool[signature_index], signature_index);
+ }
+
+ /**
+ * <pre>
+ * EnclosingMethod_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 class_index
+ * u2 method_index;
+ * }
+ *
+ * </pre>
+ */
+ void doEnclosingMethod(DataInputStream in, String indent)
+ throws IOException {
+ int class_index = in.readUnsignedShort();
+ int method_index = in.readUnsignedShort();
+ ps.printf("%-30s %s(#%d/c) %s\n", //
+ indent + "enclosing method", //
+ pool[((Integer) pool[class_index]).intValue()], //
+ class_index, //
+ (method_index == 0 ? "<>" : pool[method_index]));
+ }
+
+ /**
+ * <pre>
+ * Exceptions_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_exceptions;
+ * u2 exception_index_table[number_of_exceptions];
+ * }
+ * </pre>
+ *
+ * @param in
+ * @param indent
+ */
+ private void doExceptions(DataInputStream in, String indent)
+ throws IOException {
+ int number_of_exceptions = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "number of exceptions",
+ number_of_exceptions);
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+ for (int i = 0; i < number_of_exceptions; i++) {
+ int exception_index_table = in.readUnsignedShort();
+ sb.append(del);
+ sb.append(pool[((Integer) pool[exception_index_table])]);
+ sb.append("(#");
+ sb.append(exception_index_table);
+ sb.append("/c)");
+ del = ", ";
+ }
+ ps.printf("%-30s %d: %s\n", indent + "exceptions",
+ number_of_exceptions, sb);
+ }
+
+ /**
+ * <pre>
+ * Code_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 max_stack;
+ * u2 max_locals;
+ * u4 code_length;
+ * u1 code[code_length];
+ * u2 exception_table_length;
+ * { u2 start_pc;
+ * u2 end_pc;
+ * u2 handler_pc;
+ * u2 catch_type;
+ * } exception_table[exception_table_length];
+ * u2 attributes_count;
+ * attribute_info attributes[attributes_count];
+ * }
+ * </pre>
+ *
+ * @param in
+ * @param pool
+ * @throws IOException
+ */
+ private void doCode(DataInputStream in, String indent) throws IOException {
+ int max_stack = in.readUnsignedShort();
+ int max_locals = in.readUnsignedShort();
+ int code_length = in.readInt();
+ ps.printf(NUM_COLUMN, indent + "max_stack", max_stack);
+ ps.printf(NUM_COLUMN, indent + "max_locals", max_locals);
+ ps.printf(NUM_COLUMN, indent + "code_length", code_length);
+ byte code[] = new byte[code_length];
+ in.readFully(code);
+ printHex(code);
+ int exception_table_length = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "exception_table_length",
+ exception_table_length);
+
+ for (int i = 0; i < exception_table_length; i++) {
+ int start_pc = in.readUnsignedShort();
+ int end_pc = in.readUnsignedShort();
+ int handler_pc = in.readUnsignedShort();
+ int catch_type = in.readUnsignedShort();
+ ps.printf("%-30s %d/%d/%d/%d\n", indent + "exception_table",
+ start_pc, end_pc, handler_pc, catch_type);
+ }
+ doAttributes(in, indent + " ");
+ }
+
+ /**
+ * We must find Class.forName references ...
+ *
+ * @param code
+ */
+ protected void printHex(byte[] code) {
+ int index = 0;
+ while (index < code.length) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 16 && index < code.length; i++) {
+ String s = Integer.toHexString((0xFF & code[index++]))
+ .toUpperCase();
+ if (s.length() == 1)
+ sb.append("0");
+ sb.append(s);
+ sb.append(" ");
+ }
+ ps.printf(STR_COLUMN, "", sb.toString());
+ }
+ }
+
+ private void doSourceFile(DataInputStream in, String indent)
+ throws IOException {
+ int sourcefile_index = in.readUnsignedShort();
+ ps.printf("%-30s %s(#%d)\n", indent + "Source file",
+ pool[sourcefile_index], sourcefile_index);
+ }
+
+ private void doAnnotations(DataInputStream in, String indent)
+ throws IOException {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ ps
+ .printf(NUM_COLUMN, indent + "Number of annotations",
+ num_annotations);
+ for (int a = 0; a < num_annotations; a++) {
+ doAnnotation(in, indent);
+ }
+ }
+
+ private void doAnnotation(DataInputStream in, String indent)
+ throws IOException {
+ int type_index = in.readUnsignedShort();
+ ps.printf("%-30s %s(#%d)", indent + "type", pool[type_index],
+ type_index);
+ int num_element_value_pairs = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "num_element_value_pairs",
+ num_element_value_pairs);
+ for (int v = 0; v < num_element_value_pairs; v++) {
+ int element_name_index = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "element_name_index",
+ element_name_index);
+ doElementValue(in, indent);
+ }
+ }
+
+ private void doElementValue(DataInputStream in, String indent)
+ throws IOException {
+ int tag = in.readUnsignedByte();
+ switch (tag) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ case 's':
+ int const_value_index = in.readUnsignedShort();
+ ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag,
+ pool[const_value_index], const_value_index);
+ break;
+
+ case 'e':
+ int type_name_index = in.readUnsignedShort();
+ int const_name_index = in.readUnsignedShort();
+ ps.printf("%-30s %c %s(#%d) %s(#%d)\n", indent + "type+const", tag,
+ pool[type_name_index], type_name_index,
+ pool[const_name_index], const_name_index);
+ break;
+
+ case 'c':
+ int class_info_index = in.readUnsignedShort();
+ ps.printf("%-30s %c %s(#%d)\n", indent + "element value", tag,
+ pool[class_info_index], class_info_index);
+ break;
+
+ case '@':
+ ps.printf("%-30s %c\n", indent + "sub annotation", tag);
+ doAnnotation(in, indent);
+ break;
+
+ case '[':
+ int num_values = in.readUnsignedShort();
+ ps.printf("%-30s %c num_values=%d\n", indent + "sub element value",
+ tag, num_values);
+ for (int i = 0; i < num_values; i++) {
+ doElementValue(in, indent);
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "Invalid value for Annotation ElementValue tag " + tag);
+ }
+ }
+
+ /**
+ * <pre>
+ * LineNumberTable_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 line_number_table_length;
+ * { u2 start_pc;
+ * u2 line_number;
+ * } line_number_table[line_number_table_length];
+ * }
+ *
+ * </pre>
+ */
+ void doLineNumberTable(DataInputStream in, String indent)
+ throws IOException {
+ int line_number_table_length = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "line number table length",
+ line_number_table_length);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < line_number_table_length; i++) {
+ int start_pc = in.readUnsignedShort();
+ int line_number = in.readUnsignedShort();
+ sb.append(start_pc);
+ sb.append("/");
+ sb.append(line_number);
+ sb.append(" ");
+ }
+ ps.printf("%-30s %d: %s\n", indent + "line number table",
+ line_number_table_length, sb);
+ }
+
+ /**
+ *
+ * <pre>
+ * LocalVariableTable_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 local_variable_table_length;
+ * { u2 start_pc;
+ * u2 length;
+ * u2 name_index;
+ * u2 descriptor_index;
+ * u2 index;
+ * } local_variable_table[local_variable_table_length];
+ * }
+ * </pre>
+ */
+
+ void doLocalVariableTable(DataInputStream in, String indent)
+ throws IOException {
+ int local_variable_table_length = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "local variable table length",
+ local_variable_table_length);
+ for (int i = 0; i < local_variable_table_length; i++) {
+ int start_pc = in.readUnsignedShort();
+ int length = in.readUnsignedShort();
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ int index = in.readUnsignedShort();
+ ps.printf("%-30s %d: %d/%d %s(#%d) %s(#%d)\n", indent, index,
+ start_pc, length, pool[name_index], name_index,
+ pool[descriptor_index], descriptor_index);
+ }
+ }
+
+ /**
+ * <pre>
+ * InnerClasses_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 number_of_classes;
+ * { u2 inner_class_info_index;
+ * u2 outer_class_info_index;
+ * u2 inner_name_index;
+ * u2 inner_class_access_flags;
+ * } classes[number_of_classes];
+ * }
+ * </pre>
+ *
+ */
+ void doInnerClasses(DataInputStream in, String indent) throws IOException {
+ int number_of_classes = in.readUnsignedShort();
+ ps.printf(NUM_COLUMN, indent + "number of classes", number_of_classes);
+ for (int i = 0; i < number_of_classes; i++) {
+ int inner_class_info_index = in.readUnsignedShort();
+ int outer_class_info_index = in.readUnsignedShort();
+ int inner_name_index = in.readUnsignedShort();
+ int inner_class_access_flags = in.readUnsignedShort();
+ printAccess(inner_class_access_flags);
+
+ String iname = "<>";
+ String oname = iname;
+
+ if (inner_class_info_index != 0)
+ iname = (String) pool[((Integer) pool[inner_class_info_index])
+ .intValue()];
+ if (outer_class_info_index != 0)
+ oname = (String) pool[((Integer) pool[outer_class_info_index])
+ .intValue()];
+
+ ps.printf("%-30s %d: %x %s(#%d/c) %s(#%d/c) %s(#%d) \n", indent, i,
+ inner_class_access_flags, iname, inner_class_info_index,
+ oname, outer_class_info_index, pool[inner_name_index],
+ inner_name_index);
+ }
+ }
+
+
+ void printClassAccess(int mod) {
+ ps.printf("%-30s", "Class Access");
+ if ((ACC_PUBLIC&mod)!= 0)
+ ps.print(" public");
+ if ((ACC_FINAL&mod)!= 0)
+ ps.print(" final");
+ if ((ACC_SUPER&mod)!= 0)
+ ps.print(" super");
+ if ((ACC_INTERFACE&mod)!= 0)
+ ps.print(" interface");
+ if ((ACC_ABSTRACT&mod)!= 0)
+ ps.print(" abstract");
+
+ ps.println();
+ }
+
+ void printAccess(int mod) {
+ ps.printf("%-30s", "Access");
+ if (Modifier.isStatic(mod))
+ ps.print(" static");
+ if (Modifier.isAbstract(mod))
+ ps.print(" abstract");
+ if (Modifier.isPublic(mod))
+ ps.print(" public");
+ if (Modifier.isFinal(mod))
+ ps.print(" final");
+ if (Modifier.isInterface(mod))
+ ps.print(" interface");
+ if (Modifier.isNative(mod))
+ ps.print(" native");
+ if (Modifier.isPrivate(mod))
+ ps.print(" private");
+ if (Modifier.isProtected(mod))
+ ps.print(" protected");
+ if (Modifier.isStrict(mod))
+ ps.print(" strict");
+ if (Modifier.isSynchronized(mod))
+ ps.print(" synchronized");
+ if (Modifier.isTransient(mod))
+ ps.print(" transient");
+ if (Modifier.isVolatile(mod))
+ ps.print(" volatile");
+
+ ps.println();
+ }
+
+ public static void main(String args[]) throws Exception {
+ if (args.length == 0) {
+ System.err.println("clsd <class file>+");
+ }
+ for (int i = 0; i < args.length; i++) {
+ File f = new File(args[i]);
+ if (!f.isFile())
+ System.err.println("File does not exist or is directory " + f);
+ else {
+ ClassDumper cd = new ClassDumper(args[i]);
+ cd.dump(null);
+ }
+ }
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/classdump/packageinfo b/bundleplugin/src/main/java/aQute/libg/classdump/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/classdump/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/classloaders/URLClassLoaderWrapper.java b/bundleplugin/src/main/java/aQute/libg/classloaders/URLClassLoaderWrapper.java
new file mode 100644
index 0000000..10ddca5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/classloaders/URLClassLoaderWrapper.java
@@ -0,0 +1,27 @@
+package aQute.libg.classloaders;
+
+import java.lang.reflect.*;
+import java.net.*;
+
+public class URLClassLoaderWrapper {
+ final URLClassLoader loader;
+ final Method addURL;
+
+ public URLClassLoaderWrapper(ClassLoader loader) throws Exception {
+ this.loader = (URLClassLoader) loader;
+ addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+ addURL.setAccessible(true);
+ }
+
+ public void addURL(URL url) throws Exception {
+ try {
+ addURL.invoke(loader, url);
+ } catch( InvocationTargetException ite) {
+ throw (Exception) ite.getTargetException();
+ }
+ }
+
+ public Class<?> loadClass(String name) throws Exception {
+ return loader.loadClass(name);
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/classloaders/packageinfo b/bundleplugin/src/main/java/aQute/libg/classloaders/packageinfo
new file mode 100644
index 0000000..9ad81f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/classloaders/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
diff --git a/bundleplugin/src/main/java/aQute/libg/clauses/Clause.java b/bundleplugin/src/main/java/aQute/libg/clauses/Clause.java
new file mode 100755
index 0000000..bab9fee
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/clauses/Clause.java
@@ -0,0 +1,8 @@
+package aQute.libg.clauses;
+
+import java.util.*;
+
+public class Clause extends LinkedHashMap<String,String> {
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/clauses/Clauses.java b/bundleplugin/src/main/java/aQute/libg/clauses/Clauses.java
new file mode 100755
index 0000000..12eba4f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/clauses/Clauses.java
@@ -0,0 +1,65 @@
+package aQute.libg.clauses;
+
+import java.util.*;
+
+import aQute.libg.log.*;
+import aQute.libg.qtokens.*;
+
+public class Clauses extends LinkedHashMap<String,Map<String,String>>{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 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
+ * @return
+ * @throws MojoExecutionException
+ */
+ static public Clauses parse(String value, Logger logger) {
+ if (value == null || value.trim().length() == 0)
+ return new Clauses();
+
+ Clauses result = new Clauses();
+ QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+ char del;
+ do {
+ boolean hadAttribute = false;
+ Clause clause = new Clause();
+ List<String> aliases = new ArrayList<String>();
+ aliases.add(qt.nextToken());
+ del = qt.getSeparator();
+ while (del == ';') {
+ String adname = qt.nextToken();
+ if ((del = qt.getSeparator()) != '=') {
+ if (hadAttribute)
+ throw new IllegalArgumentException(
+ "Header contains name field after attribute or directive: "
+ + adname + " from " + value);
+ aliases.add(adname);
+ } else {
+ String advalue = qt.nextToken();
+ clause.put(adname, advalue);
+ del = qt.getSeparator();
+ hadAttribute = true;
+ }
+ }
+ for (Iterator<String> i = aliases.iterator(); i.hasNext();) {
+ String packageName = i.next();
+ if (result.containsKey(packageName)) {
+ if (logger != null)
+ logger
+ .warning("Duplicate package name in header: "
+ + packageName
+ + ". Multiple package names in one clause not supported in Bnd.");
+ } else
+ result.put(packageName, clause);
+ }
+ } while (del == ',');
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java b/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java
new file mode 100755
index 0000000..eaa806c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java
@@ -0,0 +1,129 @@
+/*
+ * $Header: /cvsroot/bnd/aQute.libg/src/aQute/libg/clauses/Selector.java,v 1.1 2009/01/19 14:17:42 pkriens Exp $
+ *
+ * Copyright (c) OSGi Alliance (2006). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aQute.libg.clauses;
+
+import java.util.*;
+import java.util.regex.*;
+
+public class Selector {
+ Pattern pattern;
+ String instruction;
+ boolean negated;
+ Clause clause;
+
+ public Selector(String instruction, boolean negated) {
+ this.instruction = instruction;
+ this.negated = negated;
+ }
+
+ public boolean matches(String value) {
+ if (pattern == null) {
+ pattern = Pattern.compile(instruction);
+ }
+ Matcher m = pattern.matcher(value);
+ return m.matches();
+ }
+
+ public boolean isNegated() {
+ return negated;
+ }
+
+ public String getPattern() {
+ return instruction;
+ }
+
+ /**
+ * Convert a string based pattern to a regular expression based pattern.
+ * This is called an instruction, this object makes it easier to handle the
+ * different cases
+ *
+ * @param string
+ * @return
+ */
+ public static Selector getPattern(String string) {
+ boolean negated = false;
+ if (string.startsWith("!")) {
+ negated = true;
+ string = string.substring(1);
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int c = 0; c < string.length(); c++) {
+ switch (string.charAt(c)) {
+ case '.':
+ sb.append("\\.");
+ break;
+ case '*':
+ sb.append(".*");
+ break;
+ case '?':
+ sb.append(".?");
+ break;
+ default:
+ sb.append(string.charAt(c));
+ break;
+ }
+ }
+ string = sb.toString();
+ if (string.endsWith("\\..*")) {
+ sb.append("|");
+ sb.append(string.substring(0, string.length() - 4));
+ }
+ return new Selector(sb.toString(), negated);
+ }
+
+ public String toString() {
+ return getPattern();
+ }
+
+ public Clause getClause() {
+ return clause;
+ }
+
+ public void setClause(Clause clause) {
+ this.clause = clause;
+ }
+
+ public static List<Selector> getInstructions(Clauses clauses) {
+ List<Selector> result = new ArrayList<Selector>();
+ for (Map.Entry<String, Map<String, String>> entry : clauses.entrySet()) {
+ Selector instruction = getPattern(entry.getKey());
+ result.add(instruction);
+ }
+ return result;
+ }
+
+ public static <T> List<T> select(Collection<T> domain,
+ List<Selector> instructions) {
+ List<T> result = new ArrayList<T>();
+ Iterator<T> iterator = domain.iterator();
+ value: while (iterator.hasNext()) {
+ T value = iterator.next();
+ for (Selector instruction : instructions) {
+ if (instruction.matches(value.toString())) {
+ if (!instruction.isNegated())
+ result.add(value);
+ continue value;
+ }
+ }
+ }
+ return result;
+ }
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/clauses/packageinfo b/bundleplugin/src/main/java/aQute/libg/clauses/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/clauses/packageinfo
@@ -0,0 +1 @@
+version 1.0
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..0a79a58
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/command/Command.java
@@ -0,0 +1,259 @@
+package aQute.libg.command;
+
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
+
+import aQute.libg.reporter.*;
+
+public class Command {
+
+ boolean trace;
+ Reporter reporter;
+ List<String> arguments = new ArrayList<String>();
+ Map<String, String> variables = new LinkedHashMap<String, String>();
+ long timeout = 0;
+ File cwd = new File("").getAbsoluteFile();
+ static Timer timer = new Timer(Command.class.getName(), true);
+ Process process;
+ volatile boolean timedout;
+ String fullCommand;
+
+ public Command(String fullCommand) {
+ this.fullCommand = fullCommand;
+ }
+
+ public Command() {
+ }
+
+ 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()]);
+ String vars[] = new String[variables.size()];
+ int i = 0;
+ for (Entry<String, String> s : variables.entrySet()) {
+ vars[i++] = s.getKey() + "=" + s.getValue();
+ }
+
+ if (fullCommand == null)
+ process = Runtime.getRuntime().exec(args, vars.length == 0 ? null : vars, cwd);
+ else
+ process = Runtime.getRuntime().exec(fullCommand, vars.length == 0 ? null : vars, 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, stderr).start();
+ if (handler != null)
+ handler.start();
+
+ result = process.waitFor();
+ if (reporter != null)
+ reporter.trace("exited process.waitFor, %s", result);
+
+ }
+ 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;
+
+ Collector(InputStream inputStream, Appendable sb) {
+ this.in = inputStream;
+ this.sb = sb;
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ int c = in.read();
+ while (c >= 0) {
+ sb.append((char) c);
+ c = in.read();
+ }
+ }
+ catch( IOException e) {
+ // We assume the socket is closed
+ }
+ 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);
+ }
+ }
+ }
+ }
+
+ static class InputStreamHandler extends Thread {
+ final InputStream in;
+ final OutputStream stdin;
+
+ InputStreamHandler(InputStream in, OutputStream stdin) {
+ this.stdin = stdin;
+ this.in = in;
+ setDaemon(true);
+ }
+
+ 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?
+ }
+ finally {
+ try {
+ stdin.close();
+ }
+ catch (IOException e) {
+ // Who cares?
+ }
+ }
+ }
+ }
+
+ public Command var(String name, String value) {
+ variables.put(name, value);
+ return this;
+ }
+
+ public Command arg(String... args) {
+ add(args);
+ return this;
+ }
+
+ public Command full(String full) {
+ fullCommand = full;
+ return this;
+ }
+
+ public void inherit() {
+ ProcessBuilder pb = new ProcessBuilder();
+ for (Entry<String, String> e : pb.environment().entrySet()) {
+ var(e.getKey(), e.getValue());
+ }
+ }
+
+ public String var(String name) {
+ return variables.get(name);
+ }
+
+ 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..a2afe57
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/command/packageinfo
@@ -0,0 +1 @@
+version 2.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..ff971e8
--- /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..90e7b1a
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Digest.java
@@ -0,0 +1,44 @@
+package aQute.libg.cryptography;
+
+import java.util.*;
+
+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();
+
+
+ public boolean equals(Object other) {
+ if ( !(other instanceof Digest))
+ return false;
+
+ Digest d = (Digest) other;
+ return Arrays.equals(d.digest, digest);
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(digest);
+ }
+
+ public byte[] toByteArray() {
+ return digest();
+ }
+}
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..eeb2c8f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Digester.java
@@ -0,0 +1,47 @@
+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;
+ OutputStream out[];
+
+ public Digester(MessageDigest instance, OutputStream ... out) {
+ md = instance;
+ this.out = out;
+ }
+
+ @Override
+ public void write( byte[] buffer, int offset, int length) throws IOException{
+ md.update(buffer,offset,length);
+ for ( OutputStream o : out ) {
+ o.write(buffer, offset, length);
+ }
+ }
+ @Override
+ public void write( int b) throws IOException{
+ md.update((byte) b);
+ for ( OutputStream o : out ) {
+ o.write(b);
+ }
+ }
+
+ public MessageDigest getMessageDigest() throws Exception {
+ return md;
+ }
+
+ public T from(InputStream in) throws Exception {
+ IO.copy(in,this);
+ return digest();
+ }
+
+ public void setOutputs(OutputStream ...out) {
+ this.out = out;
+ }
+ 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..84b53ee
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/MD5.java
@@ -0,0 +1,34 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+
+
+public class MD5 extends Digest {
+ public final static String ALGORITHM = "MD5";
+
+ public static Digester<MD5> getDigester(OutputStream ... out) throws Exception {
+ return new Digester<MD5>(MessageDigest.getInstance(ALGORITHM), out) {
+
+ @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..a01f112
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/SHA1.java
@@ -0,0 +1,35 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+
+
+public class SHA1 extends Digest {
+ public final static String ALGORITHM = "SHA1";
+
+
+ public static Digester<SHA1> getDigester(OutputStream ... out ) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance(ALGORITHM);
+ return new Digester<SHA1>(md, out) {
+ @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/SHA256.java b/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java
new file mode 100644
index 0000000..5b25cf3
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/SHA256.java
@@ -0,0 +1,35 @@
+package aQute.libg.cryptography;
+
+import java.io.*;
+import java.security.*;
+
+
+
+public class SHA256 extends Digest {
+ public final static String ALGORITHM = "SHA256";
+
+
+ public static Digester<SHA256> getDigester(OutputStream ... out ) throws NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance(ALGORITHM);
+ return new Digester<SHA256>(md, out) {
+ @Override public SHA256 digest() throws Exception {
+ return new SHA256(md.digest());
+ }
+
+ @Override public SHA256 digest(byte[] bytes) {
+ return new SHA256(bytes);
+ }
+ @Override public String getAlgorithm() {
+ return ALGORITHM;
+ }
+ };
+ }
+
+ public SHA256(byte[] b) {
+ super(b, 32);
+ }
+
+
+ @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..5218e2d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/Signer.java
@@ -0,0 +1,41 @@
+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);
+ digester.write(buffer, offset, length);
+ } catch (SignatureException e) {
+ throw new IOException(e.getLocalizedMessage());
+ }
+ }
+
+ @Override public void write(int b) throws IOException {
+ try {
+ signature.update((byte) b);
+ digester.write(b);
+ } catch (SignatureException e) {
+ throw new IOException(e.getLocalizedMessage());
+ }
+ }
+
+
+ public Signature signature() throws Exception {
+ return signature;
+ }
+
+ public D digest() throws Exception {
+ return digester.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..5506ece
--- /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.getLocalizedMessage());
+ }
+ }
+
+ @Override
+ public void write( int b) throws IOException {
+ try {
+ signature.update((byte) b);
+ } catch (SignatureException e) {
+ throw new IOException(e.getLocalizedMessage());
+ }
+ }
+
+ 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..e39f616
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/cryptography/packageinfo
@@ -0,0 +1 @@
+version 1.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..7cd73a2
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/fileiterator/FileIterator.java
@@ -0,0 +1,43 @@
+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();
+ return n < dir.list().length;
+ }
+
+ public File next() {
+ if (next != null) {
+ File answer = next.next();
+ if (!next.hasNext())
+ next = null;
+ return answer;
+ }
+ 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/filelock/packageinfo b/bundleplugin/src/main/java/aQute/libg/filelock/packageinfo
new file mode 100644
index 0000000..9ad81f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filelock/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
diff --git a/bundleplugin/src/main/java/aQute/libg/filerepo/FileRepo.java b/bundleplugin/src/main/java/aQute/libg/filerepo/FileRepo.java
new file mode 100644
index 0000000..a0149bc
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filerepo/FileRepo.java
@@ -0,0 +1,106 @@
+package aQute.libg.filerepo;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.libg.version.*;
+
+public class FileRepo {
+ File root;
+ Pattern REPO_FILE = Pattern.compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+|latest)\\.(jar|lib)");
+
+ public FileRepo(File root) {
+ this.root = root;
+ }
+
+ /**
+ * Get a list of URLs to bundles that are constrained by the bsn and
+ * versionRange.
+ */
+ public File[] get(String bsn, final VersionRange versionRange) throws Exception {
+
+ //
+ // Check if the entry exists
+ //
+ File f = new File(root, bsn);
+ if (!f.isDirectory())
+ return null;
+
+ //
+ // 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.
+ //
+ return f.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ Matcher m = REPO_FILE.matcher(name);
+ if (!m.matches())
+ return false;
+ if ( versionRange == null)
+ return true;
+
+ Version v = new Version(m.group(2));
+ return versionRange.includes(v);
+ }
+ });
+ }
+
+ public List<String> list(String regex) throws Exception {
+ if (regex == null)
+ regex = ".*";
+ final Pattern pattern = Pattern.compile(regex);
+
+ String list[] = root.list(new FilenameFilter() {
+
+ public boolean accept(File dir, String name) {
+ Matcher matcher = pattern.matcher(name);
+ return matcher.matches();
+ }
+
+ });
+ return Arrays.asList(list);
+ }
+
+ public List<Version> versions(String bsn) throws Exception {
+ File dir = new File(root, bsn);
+ final List<Version> versions = new ArrayList<Version>();
+ dir.list(new FilenameFilter() {
+
+ public boolean accept(File dir, String name) {
+ Matcher m = REPO_FILE.matcher(name);
+ if (m.matches()) {
+ versions.add(new Version(m.group(2)));
+ return true;
+ }
+ return false;
+ }
+
+ });
+ return versions;
+ }
+
+ public File get(String bsn, VersionRange range, int strategy) throws Exception {
+ File[] files = get(bsn, range);
+ if (files == null || files.length == 0)
+ return null;
+
+ if (files.length == 1)
+ return files[0];
+
+ if (strategy < 0) {
+ return files[0];
+ }
+ return files[files.length - 1];
+ }
+
+ public File put(String bsn, Version version) {
+ File dir = new File(root,bsn);
+ dir.mkdirs();
+ File file = new File(dir, bsn + "-" + version.getMajor() + "." + version.getMinor() + "." + version.getMicro() + ".jar");
+ return file;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/filerepo/packageinfo b/bundleplugin/src/main/java/aQute/libg/filerepo/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/filerepo/packageinfo
@@ -0,0 +1 @@
+version 1.0
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..2559fad
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/forker/Forker.java
@@ -0,0 +1,212 @@
+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 Map<T, Job> waiting = new HashMap<T, Job>();
+ final Set<Job> executing = new HashSet<Job>();
+ final AtomicBoolean canceled = new AtomicBoolean();
+ private int count;
+
+ /**
+ * 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;
+ e.printStackTrace();
+ } finally {
+ synchronized (this) {
+ t = null;
+ }
+ Thread.interrupted(); // clear the interrupt flag
+ done(this);
+ }
+ }
+
+ /**
+ * 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) {
+ if (waiting.containsKey(target))
+ throw new IllegalArgumentException("You can only add a target once to the forker");
+
+ System.err.println("doWhen " + dependencies + " " + target);
+ Job job = new Job();
+ job.dependencies = new HashSet<T>(dependencies);
+ job.target = target;
+ job.runnable = runnable;
+ waiting.put(target, job);
+ }
+
+ public void start(long ms) throws InterruptedException {
+ check();
+ count = waiting.size();
+ System.err.println("Count " + count);
+ schedule();
+ if (ms >= 0)
+ sync(ms);
+ }
+
+ private void check() {
+ Set<T> dependencies = new HashSet<T>();
+ for (Job job : waiting.values())
+ dependencies.addAll(job.dependencies);
+ dependencies.removeAll(waiting.keySet());
+ if (dependencies.size() > 0)
+ throw new IllegalArgumentException(
+ "There are dependencies in the jobs that are not present in the targets: "
+ + dependencies);
+
+ }
+
+ public synchronized void sync(long ms) throws InterruptedException {
+ System.err.println("Waiting for sync");
+ while (count > 0) {
+ System.err.println("Waiting for sync " + count);
+ wait(ms);
+ }
+ System.err.println("Exiting sync " + count);
+ }
+
+ private void schedule() {
+ if (canceled.get())
+ return;
+
+ List<Runnable> torun = new ArrayList<Runnable>();
+ synchronized (this) {
+ for (Iterator<Job> e = waiting.values().iterator(); e.hasNext();) {
+ Job job = e.next();
+ if (job.dependencies.isEmpty()) {
+ torun.add(job);
+ executing.add(job);
+ e.remove();
+ }
+ }
+ }
+ for (Runnable r : torun)
+ executor.execute(r);
+ }
+
+ /**
+ * Called when the target has ran by the Job.
+ *
+ * @param done
+ */
+ private void done(Job done) {
+ synchronized (this) {
+ System.err.println("count = " + count);
+ executing.remove(done);
+ count--;
+ if (count == 0) {
+ System.err.println("finished");
+ notifyAll();
+ return;
+ }
+
+ for (Job job : waiting.values()) {
+ // boolean x =
+ job.dependencies.remove(done.target);
+ //System.err.println( "Removing " + done.target + " from " + job.target + " ?" + x + " " + job.dependencies.size());
+ }
+ }
+ schedule();
+ }
+
+ /**
+ * Cancel the forker.
+ *
+ * @throws InterruptedException
+ */
+ public void cancel(long ms) throws InterruptedException {
+ System.err.println("canceled " + count);
+
+ if (!canceled.getAndSet(true)) {
+ synchronized (this) {
+ for (Job job : executing) {
+ job.cancel();
+ }
+ }
+ }
+ sync(ms);
+ }
+
+ public int getCount() {
+ return count;
+ }
+}
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..ee708fd
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/generics/Create.java
@@ -0,0 +1,54 @@
+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 <K,V> Map<K, V> map(Class<K> key, Class<V> value) {
+ return Collections.checkedMap(new LinkedHashMap<K,V>(), key,value);
+ }
+
+ public static <T> List<T> list() {
+ return new ArrayList<T>();
+ }
+
+ public static <T> List<T> list(Class<T> c) {
+ return Collections.checkedList(new ArrayList<T>(),c) ;
+ }
+
+ public static <T> Set<T> set() {
+ return new HashSet<T>();
+ }
+
+ public static <T> Set<T> set(Class<T> c) {
+ return Collections.checkedSet(new HashSet<T>(),c);
+ }
+
+ 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/glob/Glob.java b/bundleplugin/src/main/java/aQute/libg/glob/Glob.java
new file mode 100644
index 0000000..c4d06a7
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/glob/Glob.java
@@ -0,0 +1,110 @@
+package aQute.libg.glob;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Glob {
+
+ private final String glob;
+ private final Pattern pattern;
+
+ public Glob(String globString) {
+ this.glob = globString;
+ this.pattern = Pattern.compile(convertGlobToRegEx(globString));
+ }
+
+ public Matcher matcher(CharSequence input) {
+ return pattern.matcher(input);
+ }
+
+ @Override
+ public String toString() {
+ return glob;
+ }
+
+ private static String convertGlobToRegEx(String line) {
+ line = line.trim();
+ int strLen = line.length();
+ StringBuilder sb = new StringBuilder(strLen);
+ // Remove beginning and ending * globs because they're useless
+ if (line.startsWith("*")) {
+ line = line.substring(1);
+ strLen--;
+ }
+ if (line.endsWith("*")) {
+ line = line.substring(0, strLen - 1);
+ strLen--;
+ }
+ boolean escaping = false;
+ int inCurlies = 0;
+ for (char currentChar : line.toCharArray()) {
+ switch (currentChar) {
+ case '*':
+ if (escaping)
+ sb.append("\\*");
+ else
+ sb.append(".*");
+ escaping = false;
+ break;
+ case '?':
+ if (escaping)
+ sb.append("\\?");
+ else
+ sb.append('.');
+ escaping = false;
+ break;
+ case '.':
+ case '(':
+ case ')':
+ case '+':
+ case '|':
+ case '^':
+ case '$':
+ case '@':
+ case '%':
+ sb.append('\\');
+ sb.append(currentChar);
+ escaping = false;
+ break;
+ case '\\':
+ if (escaping) {
+ sb.append("\\\\");
+ escaping = false;
+ } else
+ escaping = true;
+ break;
+ case '{':
+ if (escaping) {
+ sb.append("\\{");
+ } else {
+ sb.append('(');
+ inCurlies++;
+ }
+ escaping = false;
+ break;
+ case '}':
+ if (inCurlies > 0 && !escaping) {
+ sb.append(')');
+ inCurlies--;
+ } else if (escaping)
+ sb.append("\\}");
+ else
+ sb.append("}");
+ escaping = false;
+ break;
+ case ',':
+ if (inCurlies > 0 && !escaping) {
+ sb.append('|');
+ } else if (escaping)
+ sb.append("\\,");
+ else
+ sb.append(",");
+ break;
+ default:
+ escaping = false;
+ sb.append(currentChar);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/glob/packageinfo b/bundleplugin/src/main/java/aQute/libg/glob/packageinfo
new file mode 100644
index 0000000..9ad81f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/glob/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
diff --git a/bundleplugin/src/main/java/aQute/libg/gzip/GZipUtils.java b/bundleplugin/src/main/java/aQute/libg/gzip/GZipUtils.java
new file mode 100644
index 0000000..6606d0e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/gzip/GZipUtils.java
@@ -0,0 +1,60 @@
+package aQute.libg.gzip;
+
+import java.io.BufferedInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+
+public class GZipUtils {
+
+ /**
+ * Determines whether the specified stream contains gzipped data, by
+ * checking for the GZIP magic number, and returns a stream capable of
+ * reading those data.
+ *
+ * @throws IOException
+ */
+ public static InputStream detectCompression(InputStream stream) throws IOException {
+ InputStream buffered;
+ if (stream.markSupported())
+ buffered = stream;
+ else
+ buffered = new BufferedInputStream(stream);
+
+ buffered.mark(2);
+ int magic = readUShort(buffered);
+ buffered.reset();
+
+ InputStream result;
+ if (magic == GZIPInputStream.GZIP_MAGIC)
+ result = new GZIPInputStream(buffered);
+ else
+ result = buffered;
+ return result;
+ }
+
+ /*
+ * Reads unsigned short in Intel byte order.
+ */
+ private static int readUShort(InputStream in) throws IOException {
+ int b = readUByte(in);
+ return (readUByte(in) << 8) | b;
+ }
+
+ /*
+ * Reads unsigned byte.
+ */
+ private static int readUByte(InputStream in) throws IOException {
+ int b = in.read();
+ if (b == -1) {
+ throw new EOFException();
+ }
+ if (b < -1 || b > 255) {
+ // Report on this.in, not argument in; see read{Header, Trailer}.
+ throw new IOException(in.getClass().getName() + ".read() returned value out of range -1..255: " + b);
+ }
+ return b;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/gzip/packageinfo b/bundleplugin/src/main/java/aQute/libg/gzip/packageinfo
new file mode 100644
index 0000000..9ad81f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/gzip/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
diff --git a/bundleplugin/src/main/java/aQute/libg/header/Attrs.java b/bundleplugin/src/main/java/aQute/libg/header/Attrs.java
new file mode 100644
index 0000000..d11b6d1
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/Attrs.java
@@ -0,0 +1,295 @@
+package aQute.libg.header;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.collections.*;
+import aQute.libg.version.*;
+
+public class Attrs implements Map<String, String> {
+ public enum Type {
+ STRING(null), LONG(null), VERSION(null), DOUBLE(null), STRINGS(STRING), LONGS(LONG), VERSIONS(VERSION), DOUBLES(DOUBLE);
+
+ Type sub;
+
+ Type(Type sub) {
+ this.sub = sub;
+ }
+
+ }
+
+ /**
+ * <pre>
+ * Provide-Capability ::= capability ::=
+ * name-space ::= typed-attr ::= type ::= scalar ::=
+ * capability ( ',' capability )*
+ * name-space
+ * ( ’;’ directive | typed-attr )*
+ * symbolic-name
+ * extended ( ’:’ type ) ’=’ argument
+ * scalar | list
+ * ’String’ | ’Version’ | ’Long’
+ * list ::=
+ * ’List<’ scalar ’>’
+ * </pre>
+ */
+ static String EXTENDED = "[\\-0-9a-zA-Z\\._]+";
+ static String SCALAR = "String|Version|Long|Double";
+ static String LIST = "List\\s*<\\s*(" + SCALAR + ")\\s*>";
+ public static final Pattern TYPED = Pattern.compile("\\s*(" + EXTENDED + ")\\s*:\\s*("+ SCALAR + "|" + LIST + ")\\s*");
+
+ private LinkedHashMap<String, String> map;
+ private Map<String, Type> types;
+ static Map<String, String> EMPTY = Collections.emptyMap();
+
+ public Attrs(Attrs... attrs) {
+ for (Attrs a : attrs) {
+ if (a != null) {
+ putAll(a);
+ }
+ }
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ public boolean containsKey(String name) {
+ if (map == null)
+ return false;
+
+ return map.containsKey(name);
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public boolean containsKey(Object name) {
+ assert name instanceof String;
+ if (map == null)
+ return false;
+
+ return map.containsKey((String) name);
+ }
+
+ public boolean containsValue(String value) {
+ if (map == null)
+ return false;
+
+ return map.containsValue(value);
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public boolean containsValue(Object value) {
+ assert value instanceof String;
+ if (map == null)
+ return false;
+
+ return map.containsValue((String) value);
+ }
+
+ public Set<java.util.Map.Entry<String, String>> entrySet() {
+ if (map == null)
+ return EMPTY.entrySet();
+
+ return map.entrySet();
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public String get(Object key) {
+ assert key instanceof String;
+ if (map == null)
+ return null;
+
+ return map.get((String) key);
+ }
+
+ public String get(String key) {
+ if (map == null)
+ return null;
+
+ return map.get(key);
+ }
+
+ public String get(String key, String deflt) {
+ String s = get(key);
+ if (s == null)
+ return deflt;
+ return s;
+ }
+
+ public boolean isEmpty() {
+ return map == null || map.isEmpty();
+ }
+
+ public Set<String> keySet() {
+ if (map == null)
+ return EMPTY.keySet();
+
+ return map.keySet();
+ }
+
+ public String put(String key, String value) {
+ if (map == null)
+ map = new LinkedHashMap<String, String>();
+
+ Matcher m = TYPED.matcher(key);
+ if (m.matches()) {
+ key = m.group(1);
+ String type = m.group(2);
+ Type t = Type.STRING;
+
+ if ( type.startsWith("List")) {
+ type = m.group(3);
+ if ( "String".equals(type))
+ t = Type.STRINGS;
+ else if ( "Long".equals(type))
+ t = Type.LONGS;
+ else if ( "Double".equals(type))
+ t = Type.DOUBLES;
+ else if ( "Version".equals(type))
+ t = Type.VERSIONS;
+ } else {
+ if ( "String".equals(type))
+ t = Type.STRING;
+ else if ( "Long".equals(type))
+ t = Type.LONG;
+ else if ( "Double".equals(type))
+ t = Type.DOUBLE;
+ else if ( "Version".equals(type))
+ t = Type.VERSION;
+ }
+ if (types == null)
+ types = new LinkedHashMap<String, Type>();
+ types.put(key, t);
+
+ // TODO verify value?
+ }
+
+ return map.put(key, value);
+ }
+
+ public Type getType(String key) {
+ if (types == null)
+ return Type.STRING;
+ Type t = types.get(key);
+ if (t == null)
+ return Type.STRING;
+ return t;
+ }
+
+ public void putAll(Map<? extends String, ? extends String> map) {
+ for (Map.Entry<? extends String, ? extends String> e : map.entrySet())
+ put(e.getKey(), e.getValue());
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public String remove(Object var0) {
+ assert var0 instanceof String;
+ if (map == null)
+ return null;
+
+ return map.remove((String) var0);
+ }
+
+ public String remove(String var0) {
+ if (map == null)
+ return null;
+ return map.remove(var0);
+ }
+
+ public int size() {
+ if (map == null)
+ return 0;
+ return map.size();
+ }
+
+ public Collection<String> values() {
+ if (map == null)
+ return EMPTY.values();
+
+ return map.values();
+ }
+
+ public String getVersion() {
+ return get("version");
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ append(sb);
+ return sb.toString();
+ }
+
+ public void append(StringBuilder sb) {
+ String del = "";
+ for (Map.Entry<String, String> e : entrySet()) {
+ sb.append(del);
+ sb.append(e.getKey());
+ sb.append("=");
+ sb.append(e.getValue());
+ del = ";";
+ }
+ }
+
+ @Deprecated public boolean equals(Object other) {
+ return super.equals(other);
+ }
+
+ @Deprecated public int hashCode() {
+ return super.hashCode();
+ }
+
+ public boolean isEqual(Attrs o) {
+ if (this == o)
+ return true;
+
+ Attrs other = o;
+
+ if (size() != other.size())
+ return false;
+
+ if (isEmpty())
+ return true;
+
+ SortedList<String> l = new SortedList<String>(keySet());
+ SortedList<String> lo = new SortedList<String>(other.keySet());
+ if (!l.isEqual(lo))
+ return false;
+
+ for (String key : keySet()) {
+ if (!get(key).equals(other.get(key)))
+ return false;
+ }
+ return true;
+
+ }
+
+ public Object getTyped(String adname) {
+ String s = get(adname);
+ if (s == null)
+ return null;
+
+ Type t = getType(adname);
+ return convert(t, s);
+ }
+
+ private Object convert(Type t, String s) {
+ if (t.sub == null) {
+ switch (t) {
+ case STRING:
+ return s;
+ case LONG:
+ return Long.parseLong(s.trim());
+ case VERSION:
+ return Version.parseVersion(s);
+ }
+ return null;
+ }
+ List<Object> list = new ArrayList<Object>();
+ String split[] = s.split("\\s*\\(\\?!\\),\\s*");
+ for (String p : split) {
+ p = p.replaceAll("\\\\", "");
+ list.add(convert(t.sub, p));
+ }
+ return list;
+ }
+}
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 100755
index 0000000..7b26f6f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
@@ -0,0 +1,138 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+import aQute.libg.qtokens.*;
+import aQute.libg.reporter.*;
+
+public class OSGiHeader {
+
+ static public Parameters 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 Parameters parseHeader(String value, Reporter logger) {
+ return parseHeader(value, logger, new Parameters());
+ }
+
+ static public Parameters parseHeader(String value, Reporter logger, Parameters result) {
+ if (value == null || value.trim().length() == 0)
+ return result;
+
+ QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+ char del = 0;
+ do {
+ boolean hadAttribute = false;
+ Attrs clause = new Attrs();
+ 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 Attrs parseProperties(String input) {
+ return parseProperties(input, null);
+ }
+
+ public static Attrs parseProperties(String input, Reporter logger) {
+ if (input == null || input.trim().length() == 0)
+ return new Attrs();
+
+ Attrs result = new Attrs();
+ 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);
+ logger.error("Invalid syntax for properties: " + input);
+ }
+
+ return result;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/header/Parameters.java b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
new file mode 100644
index 0000000..96f0d08
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
@@ -0,0 +1,203 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.lib.collections.*;
+import aQute.libg.reporter.*;
+
+public class Parameters implements Map<String, Attrs> {
+ private LinkedHashMap<String, Attrs> map;
+ static Map<String, Attrs> EMPTY = Collections.emptyMap();
+ String error;
+
+ public Parameters() {
+ }
+
+ public Parameters(String header) {
+ OSGiHeader.parseHeader(header, null, this);
+ }
+
+ public Parameters(String header, Reporter reporter) {
+ OSGiHeader.parseHeader(header, reporter, this);
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ public boolean containsKey(final String name) {
+ if (map == null)
+ return false;
+
+ return map.containsKey(name);
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public boolean containsKey(Object name) {
+ assert name instanceof String;
+ if (map == null)
+ return false;
+
+ return map.containsKey((String) name);
+ }
+
+ public boolean containsValue(Attrs value) {
+ if (map == null)
+ return false;
+
+ return map.containsValue(value);
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public boolean containsValue(Object value) {
+ assert value instanceof Attrs;
+ if (map == null)
+ return false;
+
+ return map.containsValue((Attrs) value);
+ }
+
+ public Set<java.util.Map.Entry<String, Attrs>> entrySet() {
+ if (map == null)
+ return EMPTY.entrySet();
+
+ return map.entrySet();
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public Attrs get(Object key) {
+ assert key instanceof String;
+ if (map == null)
+ return null;
+
+ return map.get((String) key);
+ }
+
+ public Attrs get(String key) {
+ if (map == null)
+ return null;
+
+ return map.get(key);
+ }
+
+ public boolean isEmpty() {
+ return map == null || map.isEmpty();
+ }
+
+ public Set<String> keySet() {
+ if (map == null)
+ return EMPTY.keySet();
+
+ return map.keySet();
+ }
+
+ public Attrs put(String key, Attrs value) {
+ assert key != null;
+ assert value != null;
+
+ if (map == null)
+ map = new LinkedHashMap<String, Attrs>();
+
+ return map.put(key, value);
+ }
+
+ public void putAll(Map<? extends String, ? extends Attrs> map) {
+ if (this.map == null) {
+ if (map.isEmpty())
+ return;
+ this.map = new LinkedHashMap<String, Attrs>();
+ }
+ this.map.putAll(map);
+ }
+ public void putAllIfAbsent(Map<String, ? extends Attrs> map) {
+ for(Map.Entry<String, ? extends Attrs> entry : map.entrySet() ) {
+ if ( !containsKey(entry.getKey()))
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @SuppressWarnings("cast")
+ @Deprecated public Attrs remove(Object var0) {
+ assert var0 instanceof String;
+ if (map == null)
+ return null;
+
+ return map.remove((String) var0);
+ }
+
+ public Attrs remove(String var0) {
+ if (map == null)
+ return null;
+ return map.remove(var0);
+ }
+
+ public int size() {
+ if (map == null)
+ return 0;
+ return map.size();
+ }
+
+ public Collection<Attrs> values() {
+ if (map == null)
+ return EMPTY.values();
+
+ return map.values();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ append(sb);
+ return sb.toString();
+ }
+
+ public void append(StringBuilder sb) {
+ String del = "";
+ for (Map.Entry<String, Attrs> s : entrySet()) {
+ sb.append(del);
+ sb.append(s.getKey());
+ if (!s.getValue().isEmpty()) {
+ sb.append(';');
+ s.getValue().append(sb);
+ }
+
+ del = ",";
+ }
+ }
+
+ @Deprecated
+ public boolean equals(Object other) {
+ return super.equals(other);
+ }
+
+ @Deprecated
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+
+ public boolean isEqual(Parameters other) {
+ if (this == other)
+ return true;
+
+ if (size() != other.size())
+ return false;
+
+ if (isEmpty())
+ return true;
+
+ SortedList<String> l = new SortedList<String>(keySet());
+ SortedList<String> lo = new SortedList<String>(other.keySet());
+ if (!l.isEqual(lo))
+ return false;
+
+ for (String key : keySet()) {
+ if (!get(key).isEqual(other.get(key)))
+ return false;
+ }
+ return true;
+ }
+
+ public Map<String,? extends Map<String,String>> asMapMap() {
+ return this;
+ }
+}
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..e39f616
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/header/packageinfo
@@ -0,0 +1 @@
+version 1.1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/log/Logger.java b/bundleplugin/src/main/java/aQute/libg/log/Logger.java
new file mode 100755
index 0000000..bb87707
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/log/Logger.java
@@ -0,0 +1,16 @@
+package aQute.libg.log;
+
+import java.util.*;
+
+
+public interface Logger {
+ void error(String s, Object ... args);
+ void warning(String s, Object ... args);
+ void progress(String s, Object ... args);
+
+ List<String> getWarnings();
+ List<String> getErrors();
+ List<String> getProgress();
+
+ boolean isPedantic();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/log/packageinfo b/bundleplugin/src/main/java/aQute/libg/log/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/log/packageinfo
@@ -0,0 +1 @@
+version 1.0
diff --git a/bundleplugin/src/main/java/aQute/libg/map/MAP.java b/bundleplugin/src/main/java/aQute/libg/map/MAP.java
new file mode 100644
index 0000000..41c588b
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/map/MAP.java
@@ -0,0 +1,45 @@
+package aQute.libg.map;
+
+import java.util.*;
+
+/**
+ * Easy way to build a map:
+ *
+ * Map<String,Integer> s = MAP.$("a",2).$("b",3);
+ *
+ */
+public class MAP {
+
+ static public class MAPX<K, V> extends LinkedHashMap<K, V> {
+ private static final long serialVersionUID = 1L;
+ public MAPX<K, V> $(K key, V value) {
+ put(key, value);
+ return this;
+ }
+
+ public MAPX<K, V> $(Map<K,V> all) {
+ putAll(all);
+ return this;
+ }
+ public Hashtable<K,V> asHashtable() {
+ return new Hashtable<K,V>(this);
+ }
+ }
+
+ public static <Kx, Vx> MAPX<Kx, Vx> $(Kx key, Vx value) {
+ MAPX<Kx, Vx> map = new MAPX<Kx, Vx>();
+ map.put(key, value);
+ return map;
+ }
+
+
+ public <K,V> Map<K,V> dictionary(Dictionary<K,V> dict ) {
+ Map<K,V> map = new LinkedHashMap<K, V>();
+ for ( Enumeration<K> e = dict.keys(); e.hasMoreElements(); ) {
+ K k = e.nextElement();
+ V v = dict.get(k);
+ map.put(k,v);
+ }
+ return map;
+ }
+}
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/libg/map/packageinfo b/bundleplugin/src/main/java/aQute/libg/map/packageinfo
new file mode 100644
index 0000000..897578f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/map/packageinfo
@@ -0,0 +1 @@
+version 1.2.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 100755
index 0000000..58ea539
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
@@ -0,0 +1,116 @@
+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;
+
+ StringBuilder sb = new StringBuilder();
+
+ while (index < string.length()) {
+ char c = string.charAt(index++);
+
+ if ( Character.isWhitespace(c)) {
+ if ( index == string.length())
+ break;
+ 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(StringBuilder 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 100755
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/ReporterAdapter.java b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
new file mode 100644
index 0000000..249b776
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
@@ -0,0 +1,182 @@
+package aQute.libg.reporter;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.libg.generics.*;
+
+/**
+ * Mainly used for testing where reporters are needed.
+ *
+ */
+public class ReporterAdapter implements Reporter {
+ final List<String> errors = new ArrayList<String>();
+ final List<String> warnings = new ArrayList<String>();
+ final Formatter out;
+ boolean trace;
+ boolean pedantic;
+ boolean exceptions;
+
+ /**
+ * @return the exceptions
+ */
+ public boolean isExceptions() {
+ return exceptions;
+ }
+
+ /**
+ * @param exceptions
+ * the exceptions to set
+ */
+ public void setExceptions(boolean exceptions) {
+ this.exceptions = exceptions;
+ }
+
+ /**
+ * @return the out
+ */
+ public Formatter getOut() {
+ return out;
+ }
+
+ /**
+ * @return the trace
+ */
+ public boolean isTrace() {
+ return trace;
+ }
+
+ /**
+ * @param pedantic
+ * the pedantic to set
+ */
+ public void setPedantic(boolean pedantic) {
+ this.pedantic = pedantic;
+ }
+
+ public ReporterAdapter() {
+ out = null;
+ }
+
+ public ReporterAdapter(Appendable app) {
+ out = new Formatter(app);
+ }
+
+ public void error(String s, Object... args) {
+ String e = String.format(s, args);
+ errors.add(e);
+ trace("ERROR: %s", e);
+ }
+
+ public void exception(Throwable t, String s, Object... args) {
+ String e = String.format(s, args);
+ errors.add(e);
+ trace("ERROR: %s", e);
+ if (isExceptions() || isTrace())
+ if ( t instanceof InvocationTargetException )
+ t.getCause().printStackTrace(System.err);
+ else
+ t.printStackTrace(System.err);
+ }
+
+ public void warning(String s, Object... args) {
+ String e = String.format(s, args);
+ warnings.add(e);
+ trace("warning: %s", e);
+ }
+
+ public void progress(String s, Object... args) {
+ if (out != null) {
+ out.format(s, args);
+ if ( !s.endsWith("\n"))
+ out.format("\n");
+ }
+ }
+
+ public void trace(String s, Object... args) {
+ if (trace && out != null) {
+ out.format("# "+s+"\n", args);
+ out.flush();
+ }
+ }
+
+ public List<String> getWarnings() {
+ return warnings;
+ }
+
+ public List<String> getErrors() {
+ return errors;
+ }
+
+ public boolean isPedantic() {
+ return false;
+ }
+
+ public void setTrace(boolean b) {
+ this.trace = b;
+ }
+
+ public boolean isOk() {
+ return errors.isEmpty();
+ }
+
+ public boolean isPerfect() {
+ return isOk() && warnings.isEmpty();
+ }
+
+ public boolean check(String... pattern) {
+ Set<String> missed = Create.set();
+
+ if (pattern != null) {
+ for (String p : pattern) {
+ boolean match = false;
+ Pattern pat = Pattern.compile(p);
+ for (Iterator<String> i = errors.iterator(); i.hasNext();) {
+ if (pat.matcher(i.next()).find()) {
+ i.remove();
+ match = true;
+ }
+ }
+ for (Iterator<String> i = warnings.iterator(); i.hasNext();) {
+ if (pat.matcher(i.next()).find()) {
+ i.remove();
+ match = true;
+ }
+ }
+ if (!match)
+ missed.add(p);
+
+ }
+ }
+ if (missed.isEmpty() && isPerfect())
+ return true;
+
+ if (!missed.isEmpty())
+ error("Missed the following patterns in the warnings or errors: %s", missed);
+
+ return false;
+ }
+
+ /**
+ * Report the errors and warnings
+ */
+
+ public void report(Appendable out) {
+ Formatter f = new Formatter(out);
+ report("Error", getErrors(), f);
+ report("Warning", getWarnings(), f);
+ f.flush();
+ }
+
+ void report(String title, Collection<String> list, Formatter f) {
+ if (list.isEmpty())
+ return;
+ f.format(title + (list.size() > 1 ? "s" : "")+"\n");
+ int n=0;
+ for (String s : list) {
+ f.format("%3s. %s\n", n++, s);
+ }
+ }
+
+}
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..ef7df68
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/packageinfo
@@ -0,0 +1 @@
+version 1.2
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..74f4756
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
@@ -0,0 +1,99 @@
+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;
+ boolean backup = true;
+
+ 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 Sed(File file) {
+ assert file.isFile();
+ this.file = file;
+ this.macro = null;
+ }
+
+ public void setOutput(File f) {
+ output = f;
+ }
+
+ public void replace(String pattern, String replacement) {
+ replacements.put(Pattern.compile(pattern), replacement);
+ }
+
+ public int doIt() throws IOException {
+ int actions = 0;
+ BufferedReader brdr = new BufferedReader(new InputStreamReader( new FileInputStream(file),"UTF-8"));
+ File out;
+ if (output != null)
+ out = output;
+ else
+ out = new File(file.getAbsolutePath() + ".tmp");
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter( new FileOutputStream(out),"UTF-8"));
+ 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);
+ if ( macro != null)
+ tmp = Matcher.quoteReplacement(macro.process(tmp));
+ m.appendReplacement(sb, tmp);
+ actions++;
+ }
+ m.appendTail(sb);
+
+ line = sb.toString();
+ }
+ pw.println(line);
+ }
+ pw.close();
+ if (output == null) {
+ if ( backup ) {
+ File bak = new File(file.getAbsolutePath() + ".bak");
+ file.renameTo(bak);
+ }
+ out.renameTo(file);
+ }
+ } finally {
+ brdr.close();
+ pw.close();
+ }
+ return actions;
+ }
+
+ 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();
+ }
+
+ public void setBackup(boolean b) {
+ this.backup=b;
+ }
+}
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..b3d1f97
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/packageinfo
@@ -0,0 +1 @@
+version 1.0.1
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..3d05b76
--- /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> Collection<? extends Collection<T>> tarjan(Map<T, ? extends Collection<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 100755
index 0000000..d326ec4
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/Version.java
@@ -0,0 +1,166 @@
+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 static final Version emptyVersion = LOWEST;
+ public static final Version ONE = new Version(1,0,0);
+
+ 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) {
+ version = version.trim();
+ 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;
+
+ Version o = 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() {
+ StringBuilder sb = new StringBuilder();
+ 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)");
+ }
+ }
+
+ public static Version parseVersion(String version) {
+ if (version == null) {
+ return LOWEST;
+ }
+
+ version = version.trim();
+ if (version.length() == 0) {
+ return LOWEST;
+ }
+
+ return new Version(version);
+
+ }
+
+ public Version getWithoutQualifier() {
+ return new Version(major,minor,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 100755
index 0000000..46f9e94
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/VersionRange.java
@@ -0,0 +1,96 @@
+package aQute.libg.version;
+
+import java.util.*;
+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();
+
+ StringBuilder sb = new StringBuilder();
+ 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;
+ }
+
+ public Iterable<Version> filter( final Iterable<Version> versions) {
+ List<Version> list = new ArrayList<Version>();
+ for ( Version v : versions) {
+ if ( includes(v))
+ list.add(v);
+ }
+ return list;
+ }
+
+}
\ 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..b3d1f97
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/version/packageinfo
@@ -0,0 +1 @@
+version 1.0.1
diff --git a/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java b/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java
new file mode 100644
index 0000000..ea0fcd0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/xslt/Transform.java
@@ -0,0 +1,44 @@
+package aQute.libg.xslt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+
+public class Transform {
+ static TransformerFactory transformerFactory = TransformerFactory
+ .newInstance();
+
+ static Map<URL, Templates> cache = new ConcurrentHashMap<URL, Templates>();
+
+ public static void transform(TransformerFactory transformerFactory, URL xslt, InputStream in, OutputStream out)
+ throws Exception {
+ if ( xslt == null )
+ throw new IllegalArgumentException("No source template specified");
+
+ Templates templates = cache.get(xslt);
+ if (templates == null) {
+ InputStream xsltIn = xslt.openStream();
+ try {
+ templates = transformerFactory
+ .newTemplates(new StreamSource(xsltIn));
+
+ cache.put(xslt, templates);
+ } finally {
+ in.close();
+ }
+ }
+ Result xmlResult = new StreamResult(out);
+ Source xmlSource = new StreamSource(in);
+ Transformer t = templates.newTransformer();
+ t.transform(xmlSource, xmlResult);
+ out.flush();
+ }
+
+ public static void transform(URL xslt, InputStream in, OutputStream out) throws Exception {
+ transform(transformerFactory,xslt, in, out);
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/xslt/packageinfo b/bundleplugin/src/main/java/aQute/libg/xslt/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/xslt/packageinfo
@@ -0,0 +1 @@
+version 1.0