You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by mc...@apache.org on 2012/07/16 15:43:44 UTC

svn commit: r1362033 [9/10] - in /felix/trunk/bundleplugin/src: main/java/aQute/bnd/build/ main/java/aQute/bnd/build/model/ main/java/aQute/bnd/build/model/clauses/ main/java/aQute/bnd/build/model/conversions/ main/java/aQute/bnd/compatibility/ main/ja...

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/WriteResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/WriteResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,83 @@
+package aQute.bnd.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+public class ZipResource implements Resource {
+	ZipFile		zip;
+	ZipEntry	entry;
+	long		lastModified;
+	String		extra;
+
+	ZipResource(ZipFile zip, ZipEntry entry, long lastModified) throws UnsupportedEncodingException {
+		this.zip = zip;
+		this.entry = entry;
+		this.lastModified = lastModified;
+		byte[] data = entry.getExtra();
+		if (data != null)
+			this.extra = new String(data, "UTF-8");
+	}
+
+	public InputStream openInputStream() throws IOException {
+		return zip.getInputStream(entry);
+	}
+
+	public String toString() {
+		return ":" + zip.getName() + "(" + entry.getName() + "):";
+	}
+
+	public static ZipFile build(Jar jar, File file) throws ZipException, IOException {
+		return build(jar, file, null);
+	}
+
+	public static ZipFile build(Jar jar, File file, Pattern pattern) throws ZipException, IOException {
+
+		try {
+			ZipFile zip = new ZipFile(file);
+			nextEntry: for (Enumeration< ? extends ZipEntry> e = zip.entries(); e.hasMoreElements();) {
+				ZipEntry entry = e.nextElement();
+				if (pattern != null) {
+					Matcher m = pattern.matcher(entry.getName());
+					if (!m.matches())
+						continue nextEntry;
+				}
+				if (!entry.isDirectory()) {
+					long time = entry.getTime();
+					if (time <= 0)
+						time = file.lastModified();
+					jar.putResource(entry.getName(), new ZipResource(zip, entry, time), true);
+				}
+			}
+			return zip;
+		}
+		catch (ZipException ze) {
+			throw new ZipException("The JAR/ZIP file (" + file.getAbsolutePath() + ") seems corrupted, error: "
+					+ ze.getMessage());
+		}
+		catch (FileNotFoundException e) {
+			throw new IllegalArgumentException("Problem opening JAR: " + file.getAbsolutePath());
+		}
+	}
+
+	public void write(OutputStream out) throws Exception {
+		FileResource.copy(this, out);
+	}
+
+	public long lastModified() {
+		return lastModified;
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+
+	public long size() {
+		return entry.getSize();
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/ZipResource.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,238 @@
+package aQute.bnd.osgi.eclipse;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.parsers.*;
+
+import org.w3c.dom.*;
+import org.xml.sax.*;
+
+import aQute.service.reporter.*;
+
+/**
+ * Parse the Eclipse project information for the classpath. Unfortunately, it is
+ * impossible to read the variables. They are ignored but that can cause
+ * problems.
+ * 
+ * @version $Revision$
+ */
+public class EclipseClasspath {
+	static DocumentBuilderFactory	documentBuilderFactory	= DocumentBuilderFactory.newInstance();
+	DocumentBuilder					db;
+	File							project;
+	File							workspace;
+	Set<File>						sources					= new LinkedHashSet<File>();
+	Set<File>						allSources				= new LinkedHashSet<File>();
+
+	Set<File>						classpath				= new LinkedHashSet<File>();
+	List<File>						dependents				= new ArrayList<File>();
+	File							output;
+	boolean							recurse					= true;
+	Set<File>						exports					= new LinkedHashSet<File>();
+	Map<String,String>				properties				= new HashMap<String,String>();
+	Reporter						reporter;
+	int								options;
+	Set<File>						bootclasspath			= new LinkedHashSet<File>();
+
+	public final static int			DO_VARIABLES			= 1;
+
+	/**
+	 * Parse an Eclipse project structure to discover the classpath.
+	 * 
+	 * @param workspace
+	 *            Points to workspace
+	 * @param project
+	 *            Points to project
+	 * @throws ParserConfigurationException
+	 * @throws SAXException
+	 * @throws IOException
+	 */
+
+	public EclipseClasspath(Reporter reporter, File workspace, File project, @SuppressWarnings("unused") int options) throws Exception {
+		this.project = project.getCanonicalFile();
+		this.workspace = workspace.getCanonicalFile();
+		this.reporter = reporter;
+		db = documentBuilderFactory.newDocumentBuilder();
+		parse(this.project, true);
+		db = null;
+	}
+
+	public EclipseClasspath(Reporter reporter, File workspace, File project) throws Exception {
+		this(reporter, workspace, project, 0);
+	}
+
+	/**
+	 * Recursive routine to parse the files. If a sub project is detected, it is
+	 * parsed before the parsing continues. This should give the right order.
+	 * 
+	 * @param project
+	 *            Project directory
+	 * @param top
+	 *            If this is the top project
+	 * @throws ParserConfigurationException
+	 * @throws SAXException
+	 * @throws IOException
+	 */
+	void parse(File project, boolean top) throws ParserConfigurationException, SAXException, IOException {
+		File file = new File(project, ".classpath");
+		if (!file.exists())
+			throw new FileNotFoundException(".classpath file not found: " + file.getAbsolutePath());
+
+		Document doc = db.parse(file);
+		NodeList nodelist = doc.getDocumentElement().getElementsByTagName("classpathentry");
+
+		if (nodelist == null)
+			throw new IllegalArgumentException("Can not find classpathentry in classpath file");
+
+		for (int i = 0; i < nodelist.getLength(); i++) {
+			Node node = nodelist.item(i);
+			NamedNodeMap attrs = node.getAttributes();
+			String kind = get(attrs, "kind");
+			if ("src".equals(kind)) {
+				String path = get(attrs, "path");
+				// TODO boolean exported = "true".equalsIgnoreCase(get(attrs,
+				// "exported"));
+				if (path.startsWith("/")) {
+					// We have another project
+					File subProject = getFile(workspace, project, path);
+					if (recurse)
+						parse(subProject, false);
+					dependents.add(subProject.getCanonicalFile());
+				} else {
+					File src = getFile(workspace, project, path);
+					allSources.add(src);
+					if (top) {
+						// We only want the sources for our own project
+						// or we'll compile all at once. Not a good idea
+						// because project settings can differ.
+						sources.add(src);
+					}
+				}
+			} else if ("lib".equals(kind)) {
+				String path = get(attrs, "path");
+				boolean exported = "true".equalsIgnoreCase(get(attrs, "exported"));
+				if (top || exported) {
+					File jar = getFile(workspace, project, path);
+					if (jar.getName().startsWith("ee."))
+						bootclasspath.add(jar);
+					else
+						classpath.add(jar);
+					if (exported)
+						exports.add(jar);
+				}
+			} else if ("output".equals(kind)) {
+				String path = get(attrs, "path");
+				path = path.replace('/', File.separatorChar);
+				output = getFile(workspace, project, path);
+				classpath.add(output);
+				exports.add(output);
+			} else if ("var".equals(kind)) {
+				boolean exported = "true".equalsIgnoreCase(get(attrs, "exported"));
+				File lib = replaceVar(get(attrs, "path"));
+				File slib = replaceVar(get(attrs, "sourcepath"));
+				if (lib != null) {
+					classpath.add(lib);
+					if (exported)
+						exports.add(lib);
+				}
+				if (slib != null)
+					sources.add(slib);
+			} else if ("con".equals(kind)) {
+				// Should do something useful ...
+			}
+		}
+	}
+
+	private File getFile(File abs, File relative, String opath) {
+		String path = opath.replace('/', File.separatorChar);
+		File result = new File(path);
+		if (result.isAbsolute() && result.isFile()) {
+			return result;
+		}
+		if (path.startsWith(File.separator)) {
+			result = abs;
+			path = path.substring(1);
+		} else
+			result = relative;
+
+		StringTokenizer st = new StringTokenizer(path, File.separator);
+		while (st.hasMoreTokens()) {
+			String token = st.nextToken();
+			result = new File(result, token);
+		}
+
+		if (!result.exists())
+			System.err.println("File not found: project=" + project + " workspace=" + workspace + " path=" + opath
+					+ " file=" + result);
+		return result;
+	}
+
+	static Pattern	PATH	= Pattern.compile("([A-Z_]+)/(.*)");
+
+	private File replaceVar(String path) {
+		if ((options & DO_VARIABLES) == 0)
+			return null;
+
+		Matcher m = PATH.matcher(path);
+		if (m.matches()) {
+			String var = m.group(1);
+			String remainder = m.group(2);
+			String base = properties.get(var);
+			if (base != null) {
+				File b = new File(base);
+				File f = new File(b, remainder.replace('/', File.separatorChar));
+				return f;
+			}
+			reporter.error("Can't find replacement variable for: " + path);
+		} else
+			reporter.error("Cant split variable path: " + path);
+		return null;
+	}
+
+	private String get(NamedNodeMap map, String name) {
+		Node node = map.getNamedItem(name);
+		if (node == null)
+			return null;
+
+		return node.getNodeValue();
+	}
+
+	public Set<File> getClasspath() {
+		return classpath;
+	}
+
+	public Set<File> getSourcepath() {
+		return sources;
+	}
+
+	public File getOutput() {
+		return output;
+	}
+
+	public List<File> getDependents() {
+		return dependents;
+	}
+
+	public void setRecurse(boolean recurse) {
+		this.recurse = recurse;
+	}
+
+	public Set<File> getExports() {
+		return exports;
+	}
+
+	public void setProperties(Map<String,String> map) {
+		this.properties = map;
+	}
+
+	public Set<File> getBootclasspath() {
+		return bootclasspath;
+	}
+
+	public Set<File> getAllSources() {
+		return allSources;
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java
------------------------------------------------------------------------------
    svn:executable = *

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/eclipse/EclipseClasspath.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/packageinfo Mon Jul 16 13:43:38 2012
@@ -0,0 +1 @@
+version 2.0.0

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,103 @@
+package aQute.bnd.osgi.resource;
+
+import java.util.*;
+
+import org.osgi.resource.*;
+
+class CapReq implements Capability, Requirement {
+	
+	static enum MODE { Capability, Requirement }
+	
+	private final MODE mode;
+	private final String	namespace;
+	private final Resource	resource;
+	private final Map<String,String>	directives;
+	private final Map<String,Object>	attributes;
+
+	CapReq(MODE mode, String namespace, Resource resource, Map<String, String> directives, Map<String, Object> attributes) {
+		this.mode = mode;
+		this.namespace = namespace;
+		this.resource = resource;
+		this.directives = new HashMap<String,String>(directives);
+		this.attributes = new HashMap<String,Object>(attributes);
+	}
+
+	public String getNamespace() {
+		return namespace;
+	}
+
+	public Map<String,String> getDirectives() {
+		return Collections.unmodifiableMap(directives);
+	}
+
+	public Map<String,Object> getAttributes() {
+		return Collections.unmodifiableMap(attributes);
+	}
+
+	public Resource getResource() {
+		return resource;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+		result = prime * result + ((directives == null) ? 0 : directives.hashCode());
+		result = prime * result + ((mode == null) ? 0 : mode.hashCode());
+		result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
+		result = prime * result + ((resource == null) ? 0 : resource.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		CapReq other = (CapReq) obj;
+		if (attributes == null) {
+			if (other.attributes != null)
+				return false;
+		} else if (!attributes.equals(other.attributes))
+			return false;
+		if (directives == null) {
+			if (other.directives != null)
+				return false;
+		} else if (!directives.equals(other.directives))
+			return false;
+		if (mode != other.mode)
+			return false;
+		if (namespace == null) {
+			if (other.namespace != null)
+				return false;
+		} else if (!namespace.equals(other.namespace))
+			return false;
+		if (resource == null) {
+			if (other.resource != null)
+				return false;
+		} else if (!resource.equals(other.resource))
+			return false;
+		return true;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		if (mode == MODE.Capability) {
+			Object value = attributes.get(namespace);
+			builder.append(namespace).append('=').append(value);
+		} else {
+			String filter = directives.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+			builder.append(filter);
+			if (Namespace.RESOLUTION_OPTIONAL.equals(directives.get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
+				builder.append("%OPT");
+			}
+		}
+		return builder.toString();
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReq.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,91 @@
+package aQute.bnd.osgi.resource;
+
+import java.util.*;
+
+import org.osgi.framework.namespace.*;
+import org.osgi.resource.*;
+
+import aQute.bnd.osgi.resource.CapReq.MODE;
+import aQute.libg.filters.*;
+
+public class CapReqBuilder {
+
+	private final String				namespace;
+	private Resource					resource;
+	private final Map<String,Object>	attributes	= new HashMap<String,Object>();
+	private final Map<String,String>	directives	= new HashMap<String,String>();
+
+	public CapReqBuilder(String namespace) {
+		this.namespace = namespace;
+	}
+	
+	public static CapReqBuilder clone(Capability capability) {
+		CapReqBuilder builder = new CapReqBuilder(capability.getNamespace());
+		builder.addAttributes(capability.getAttributes());
+		builder.addDirectives(capability.getDirectives());
+		return builder;
+	}
+	
+	public static CapReqBuilder clone(Requirement requirement) {
+		CapReqBuilder builder = new CapReqBuilder(requirement.getNamespace());
+		builder.addAttributes(requirement.getAttributes());
+		builder.addDirectives(requirement.getDirectives());
+		return builder;
+	}
+	
+	public String getNamespace() {
+		return namespace;
+	}
+	
+	public CapReqBuilder setResource(Resource resource) {
+		this.resource = resource;
+		return this;
+	}
+
+	public CapReqBuilder addAttribute(String name, Object value) {
+		attributes.put(name, value);
+		return this;
+	}
+	
+	public CapReqBuilder addAttributes(Map<? extends String, ? extends Object> attributes) {
+		this.attributes.putAll(attributes);
+		return this;
+	}
+
+	public CapReqBuilder addDirective(String name, String value) {
+		directives.put(name, value);
+		return this;
+	}
+	
+	public CapReqBuilder addDirectives(Map<? extends String, ? extends String> directives) {
+		this.directives.putAll(directives);
+		return this;
+	}
+	
+	public Capability buildCapability() {
+		// TODO check the thrown exception
+		if (resource == null) throw new IllegalStateException("Cannot build Capability with null Resource.");
+		return new CapReq(MODE.Capability, namespace, resource, directives, attributes);
+	}
+	
+	public Requirement buildRequirement() {
+		// TODO check the thrown exception
+		if (resource == null) throw new IllegalStateException("Cannot build Requirement with null Resource.");
+		return new CapReq(MODE.Requirement, namespace, resource, directives, attributes);
+	}
+
+	public Requirement buildSyntheticRequirement() {
+		return new CapReq(MODE.Requirement, namespace, null, directives, attributes);
+	}
+	
+	public static final CapReqBuilder createPackageRequirement(String pkgName, String range) {
+		Filter filter;
+		SimpleFilter pkgNameFilter = new SimpleFilter(PackageNamespace.PACKAGE_NAMESPACE, pkgName);
+		if (range != null)
+			filter = new AndFilter().addChild(pkgNameFilter).addChild(new LiteralFilter(Filters.fromVersionRange(range)));
+		else
+			filter = pkgNameFilter;
+		
+		return new CapReqBuilder(PackageNamespace.PACKAGE_NAMESPACE).addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter.toString());
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/CapReqBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,64 @@
+package aQute.bnd.osgi.resource;
+
+import org.osgi.framework.namespace.*;
+
+import aQute.bnd.osgi.*;
+import aQute.libg.filters.*;
+
+public class Filters {
+	
+	public static final String DEFAULT_VERSION_ATTR = IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE;
+	
+	/**
+	 * Generate an LDAP-style version filter from a version range, e.g.
+	 * {@code [1.0,2.0)} generates {@code (&(version>=1.0)(!(version>=2.0))}
+	 * 
+	 * @param range
+	 * @return The generated filter.
+	 * @throws IllegalArgumentException
+	 *             If the supplied range is invalid.
+	 */
+	public static String fromVersionRange(String range) throws IllegalArgumentException {
+		return fromVersionRange(range, DEFAULT_VERSION_ATTR);
+	}
+
+	/**
+	 * Generate an LDAP-style version filter from a version range, using a
+	 * specific attribute name for the version; for example can be used to
+	 * generate a range using the {@code bundle-version} attribute such as
+	 * {@code (&(bundle-version>=1.0)(!(bundle-version>=2.0))}.
+	 * 
+	 * @param range
+	 * @param versionAttr
+	 * @return The generated filter
+	 * @throws IllegalArgumentException
+	 *             If the supplied range is invalid.
+	 */
+	public static String fromVersionRange(String range, String versionAttr) throws IllegalArgumentException {
+		if (range == null)
+			return null;
+		VersionRange parsedRange = new VersionRange(range);
+		
+		Filter left;
+		if (parsedRange.includeLow())
+			left = new SimpleFilter(versionAttr, Operator.GreaterThanOrEqual, parsedRange.getLow().toString());
+		else
+			left = new NotFilter(new SimpleFilter(versionAttr, Operator.LessThanOrEqual, parsedRange.getLow().toString()));
+		
+		Filter right;
+		if (!parsedRange.isRange())
+			right = null;
+		else if (parsedRange.includeHigh())
+			right = new SimpleFilter(versionAttr, Operator.LessThanOrEqual, parsedRange.getHigh().toString());
+		else
+			right = new NotFilter(new SimpleFilter(versionAttr, Operator.GreaterThanOrEqual, parsedRange.getHigh().toString()));
+		
+		Filter result;
+		if (right != null)
+			result = new AndFilter().addChild(left).addChild(right);
+		else
+			result = left;
+		
+		return result.toString();
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/Filters.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,55 @@
+package aQute.bnd.osgi.resource;
+
+import java.util.*;
+
+import org.osgi.resource.*;
+
+public class ResourceBuilder {
+
+	private final ResourceImpl		resource		= new ResourceImpl();
+	private final List<Capability>	capabilities	= new LinkedList<Capability>();
+	private final List<Requirement>	requirements	= new LinkedList<Requirement>();
+
+	private boolean					built			= false;
+
+	public ResourceBuilder addCapability(Capability capability) {
+		CapReqBuilder builder = CapReqBuilder.clone(capability);
+		return addCapability(builder);
+	}
+	
+	public ResourceBuilder addCapability(CapReqBuilder builder) {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+
+		Capability cap = builder.setResource(resource).buildCapability();
+		capabilities.add(cap);
+
+		return this;
+	}
+	
+	public ResourceBuilder addRequirement(Requirement requirement) {
+		CapReqBuilder builder = CapReqBuilder.clone(requirement);
+		return addRequirement(builder);
+	}
+	
+	public ResourceBuilder addRequirement(CapReqBuilder builder) {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+
+		Requirement req = builder.setResource(resource).buildRequirement();
+		requirements.add(req);
+
+		return this;
+	}
+
+	public Resource build() {
+		if (built)
+			throw new IllegalStateException("Resource already built");
+		built = true;
+
+		resource.setCapabilities(capabilities);
+		resource.setRequirements(requirements);
+		return resource;
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceBuilder.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,73 @@
+package aQute.bnd.osgi.resource;
+
+import java.util.*;
+
+import org.osgi.framework.namespace.*;
+import org.osgi.resource.*;
+
+class ResourceImpl implements Resource {
+
+	private List<Capability>				allCapabilities;
+	private Map<String,List<Capability>>	capabilityMap;
+
+	private List<Requirement>				allRequirements;
+	private Map<String,List<Requirement>>	requirementMap;
+
+	void setCapabilities(List<Capability> capabilities) {
+		allCapabilities = capabilities;
+
+		capabilityMap = new HashMap<String,List<Capability>>();
+		for (Capability capability : capabilities) {
+			List<Capability> list = capabilityMap.get(capability.getNamespace());
+			if (list == null) {
+				list = new LinkedList<Capability>();
+				capabilityMap.put(capability.getNamespace(), list);
+			}
+			list.add(capability);
+		}
+	}
+
+	public List<Capability> getCapabilities(String namespace) {
+		return namespace == null ? allCapabilities : capabilityMap.get(namespace);
+	}
+
+	void setRequirements(List<Requirement> requirements) {
+		allRequirements = requirements;
+
+		requirementMap = new HashMap<String,List<Requirement>>();
+		for (Requirement requirement : requirements) {
+			List<Requirement> list = requirementMap.get(requirement.getNamespace());
+			if (list == null) {
+				list = new LinkedList<Requirement>();
+				requirementMap.put(requirement.getNamespace(), list);
+			}
+			list.add(requirement);
+		}
+	}
+
+	public List<Requirement> getRequirements(String namespace) {
+		return namespace == null ? allRequirements : requirementMap.get(namespace);
+	}
+
+	@Override
+	public String toString() {
+		final StringBuilder builder = new StringBuilder();
+		List<Capability> identities = getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+		if (identities != null && identities.size() == 1) {
+			Capability idCap = identities.get(0);
+			Object id = idCap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE);
+			Object version = idCap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+			
+			builder.append(id).append(" ver=").append(version);
+		} else {
+			// Generic toString
+			builder.append("ResourceImpl [caps=");
+			builder.append(allCapabilities);
+			builder.append(", reqs=");
+			builder.append(allRequirements);
+			builder.append("]");
+		}
+		return builder.toString();
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/ResourceImpl.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/osgi/resource/packageinfo Mon Jul 16 13:43:38 2012
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,14 @@
+package aQute.bnd.properties;
+
+public class BadLocationException extends Exception {
+
+	private static final long	serialVersionUID	= 1L;
+
+	public BadLocationException() {
+		super();
+	}
+
+	public BadLocationException(String var0) {
+		super(var0);
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/BadLocationException.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,147 @@
+package aQute.bnd.properties;
+
+/**
+ * Copy-on-write <code>ITextStore</code> wrapper.
+ * <p>
+ * This implementation uses an unmodifiable text store for the initial content.
+ * Upon first modification attempt, the unmodifiable store is replaced with a
+ * modifiable instance which must be supplied in the constructor.
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * 
+ * @since 3.2
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class CopyOnWriteTextStore implements ITextStore {
+
+	/**
+	 * An unmodifiable String based text store. It is not possible to modify the
+	 * content other than using {@link #set}. Trying to {@link #replace} a text
+	 * range will throw an <code>UnsupportedOperationException</code>.
+	 */
+	private static class StringTextStore implements ITextStore {
+
+		/** Represents the content of this text store. */
+		private String	fText	= "";	//$NON-NLS-1$
+
+		/**
+		 * Create an empty text store.
+		 */
+		StringTextStore() {
+			super();
+		}
+
+		/**
+		 * Create a text store with initial content.
+		 * 
+		 * @param text
+		 *            the initial content
+		 */
+		StringTextStore(String text) {
+			super();
+			set(text);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#get(int)
+		 */
+		public char get(int offset) {
+			return fText.charAt(offset);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+		 */
+		public String get(int offset, int length) {
+			return fText.substring(offset, offset + length);
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#getLength()
+		 */
+		public int getLength() {
+			return fText.length();
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+		 * java.lang.String)
+		 */
+		public void replace(int offset, int length, String text) {
+			// modification not supported
+			throw new UnsupportedOperationException();
+		}
+
+		/*
+		 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+		 */
+		public void set(String text) {
+			fText = text != null ? text : ""; //$NON-NLS-1$
+		}
+
+	}
+
+	/** The underlying "real" text store */
+	protected ITextStore		fTextStore	= new StringTextStore();
+
+	/** A modifiable <code>ITextStore</code> instance */
+	private final ITextStore	fModifiableTextStore;
+
+	/**
+	 * Creates an empty text store. The given text store will be used upon first
+	 * modification attempt.
+	 * 
+	 * @param modifiableTextStore
+	 *            a modifiable <code>ITextStore</code> instance, may not be
+	 *            <code>null</code>
+	 */
+	public CopyOnWriteTextStore(ITextStore modifiableTextStore) {
+		fTextStore = new StringTextStore();
+		fModifiableTextStore = modifiableTextStore;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int)
+	 */
+	public char get(int offset) {
+		return fTextStore.get(offset);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+	 */
+	public String get(int offset, int length) {
+		return fTextStore.get(offset, length);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#getLength()
+	 */
+	public int getLength() {
+		return fTextStore.getLength();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+	 * java.lang.String)
+	 */
+	public void replace(int offset, int length, String text) {
+		if (fTextStore != fModifiableTextStore) {
+			String content = fTextStore.get(0, fTextStore.getLength());
+			fTextStore = fModifiableTextStore;
+			fTextStore.set(content);
+		}
+		fTextStore.replace(offset, length, text);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+	 */
+	public void set(String text) {
+		fTextStore = new StringTextStore(text);
+		fModifiableTextStore.set(""); //$NON-NLS-1$
+	}
+
+}
\ No newline at end of file

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/CopyOnWriteTextStore.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,59 @@
+package aQute.bnd.properties;
+
+public class Document implements IDocument {
+
+	public final static String[]	DELIMITERS	= {
+			"\r", "\n", "\r\n"
+												};
+
+	private LineTracker				lineTracker	= new LineTracker();
+	private ITextStore				textStore	= new CopyOnWriteTextStore(new GapTextStore());
+
+	public Document(String text) {
+		setText(text);
+	}
+
+	public int getNumberOfLines() {
+		return lineTracker.getNumberOfLines();
+	}
+
+	public IRegion getLineInformation(int line) throws BadLocationException {
+		return lineTracker.getLineInformation(line);
+	}
+
+	public String get(int offset, int length) throws BadLocationException {
+		return textStore.get(offset, length);
+	}
+
+	public String getLineDelimiter(int line) throws BadLocationException {
+		return lineTracker.getLineDelimiter(line);
+	}
+
+	public int getLength() {
+		return textStore.getLength();
+	}
+
+	public void replace(int offset, int length, String text) throws BadLocationException {
+		textStore.replace(offset, length, text);
+		lineTracker.set(get());
+	}
+
+	public char getChar(int pos) {
+		return textStore.get(pos);
+	}
+
+	public void setText(String text) {
+		textStore.set(text);
+		lineTracker.set(text);
+	}
+
+	public String get() {
+		return textStore.get(0, textStore.getLength());
+	}
+
+	protected static class DelimiterInfo {
+		public int		delimiterIndex;
+		public int		delimiterLength;
+		public String	delimiter;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Document.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,419 @@
+package aQute.bnd.properties;
+
+/**
+ * Implements a gap managing text store. The gap text store relies on the
+ * assumption that consecutive changes to a document are co-located. The start
+ * of the gap is always moved to the location of the last change.
+ * <p>
+ * <strong>Performance:</strong> Typing-style changes perform in constant time
+ * unless re-allocation becomes necessary. Generally, a change that does not
+ * cause re-allocation will cause at most one
+ * {@linkplain System#arraycopy(Object, int, Object, int, int) arraycopy}
+ * operation of a length of about <var>d</var>, where <var>d</var> is the
+ * distance from the previous change. Let <var>a(x)</var> be the algorithmic
+ * performance of an <code>arraycopy</code> operation of the length
+ * <var>x</var>, then such a change then performs in <i>O(a(x))</i>,
+ * {@linkplain #get(int, int) get(int, <var>length</var>)} performs in
+ * <i>O(a(length))</i>, {@link #get(int)} in <i>O(1)</i>.
+ * <p>
+ * How frequently the array needs re-allocation is controlled by the constructor
+ * parameters.
+ * </p>
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ * 
+ * @see CopyOnWriteTextStore for a copy-on-write text store wrapper
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class GapTextStore implements ITextStore {
+	/**
+	 * The minimum gap size allocated when re-allocation occurs.
+	 * 
+	 * @since 3.3
+	 */
+	private final int	fMinGapSize;
+	/**
+	 * The maximum gap size allocated when re-allocation occurs.
+	 * 
+	 * @since 3.3
+	 */
+	private final int	fMaxGapSize;
+	/**
+	 * The multiplier to compute the array size from the content length
+	 * (1&nbsp;&lt;=&nbsp;fSizeMultiplier&nbsp;&lt;=&nbsp;2).
+	 * 
+	 * @since 3.3
+	 */
+	private final float	fSizeMultiplier;
+
+	/** The store's content */
+	private char[]		fContent	= new char[0];
+	/** Starting index of the gap */
+	private int			fGapStart	= 0;
+	/** End index of the gap */
+	private int			fGapEnd		= 0;
+	/**
+	 * The current high water mark. If a change would cause the gap to grow
+	 * larger than this, the array is re-allocated.
+	 * 
+	 * @since 3.3
+	 */
+	private int			fThreshold	= 0;
+
+	/**
+	 * Creates a new empty text store using the specified low and high
+	 * watermarks.
+	 * 
+	 * @param lowWatermark
+	 *            unused - at the lower bound, the array is only resized when
+	 *            the content does not fit
+	 * @param highWatermark
+	 *            if the gap is ever larger than this, it will automatically be
+	 *            shrunken (&gt;=&nbsp;0)
+	 * @deprecated use {@link GapTextStore#GapTextStore(int, int, float)}
+	 *             instead
+	 */
+	public GapTextStore(int lowWatermark, int highWatermark) {
+		/*
+		 * Legacy constructor. The API contract states that highWatermark is the
+		 * upper bound for the gap size. Albeit this contract was not previously
+		 * adhered to, it is now: The allocated gap size is fixed at half the
+		 * highWatermark. Since the threshold is always twice the allocated gap
+		 * size, the gap will never grow larger than highWatermark. Previously,
+		 * the gap size was initialized to highWatermark, causing re-allocation
+		 * if the content length shrunk right after allocation. The fixed gap
+		 * size is now only half of the previous value, circumventing that
+		 * problem (there was no API contract specifying the initial gap size).
+		 * The previous implementation did not allow the gap size to become
+		 * smaller than lowWatermark, which doesn't make any sense: that area of
+		 * the gap was simply never ever used.
+		 */
+		this(highWatermark / 2, highWatermark / 2, 0f);
+	}
+
+	/**
+	 * Equivalent to {@linkplain GapTextStore#GapTextStore(int, int, float) new
+	 * GapTextStore(256, 4096, 0.1f)}.
+	 * 
+	 * @since 3.3
+	 */
+	public GapTextStore() {
+		this(256, 4096, 0.1f);
+	}
+
+	/**
+	 * Creates an empty text store that uses re-allocation thresholds relative
+	 * to the content length. Re-allocation is controlled by the
+	 * <em>gap factor</em>, which is the quotient of the gap size and the array
+	 * size. Re-allocation occurs if a change causes the gap factor to go
+	 * outside <code>[0,&nbsp;maxGapFactor]</code>. When re-allocation occurs,
+	 * the array is sized such that the gap factor is
+	 * <code>0.5 * maxGapFactor</code>. The gap size computed in this manner is
+	 * bounded by the <code>minSize</code> and <code>maxSize</code> parameters.
+	 * <p>
+	 * A <code>maxGapFactor</code> of <code>0</code> creates a text store that
+	 * never has a gap at all (if <code>minSize</code> is 0); a
+	 * <code>maxGapFactor</code> of <code>1</code> creates a text store that
+	 * doubles its size with every re-allocation and that never shrinks.
+	 * </p>
+	 * <p>
+	 * The <code>minSize</code> and <code>maxSize</code> parameters are absolute
+	 * bounds to the allocated gap size. Use <code>minSize</code> to avoid
+	 * frequent re-allocation for small documents. Use <code>maxSize</code> to
+	 * avoid a huge gap being allocated for large documents.
+	 * </p>
+	 * 
+	 * @param minSize
+	 *            the minimum gap size to allocate (&gt;=&nbsp;0; use 0 for no
+	 *            minimum)
+	 * @param maxSize
+	 *            the maximum gap size to allocate (&gt;=&nbsp;minSize; use
+	 *            {@link Integer#MAX_VALUE} for no maximum)
+	 * @param maxGapFactor
+	 *            is the maximum fraction of the array that is occupied by the
+	 *            gap (
+	 *            <code>0&nbsp;&lt;=&nbsp;maxGapFactor&nbsp;&lt;=&nbsp;1</code>)
+	 * @since 3.3
+	 */
+	public GapTextStore(int minSize, int maxSize, float maxGapFactor) {
+		fMinGapSize = minSize;
+		fMaxGapSize = maxSize;
+		fSizeMultiplier = 1 / (1 - maxGapFactor / 2);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int)
+	 */
+	public final char get(int offset) {
+		if (offset < fGapStart)
+			return fContent[offset];
+
+		return fContent[offset + gapSize()];
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#get(int, int)
+	 */
+	public final String get(int offset, int length) {
+		if (fGapStart <= offset)
+			return new String(fContent, offset + gapSize(), length);
+
+		final int end = offset + length;
+
+		if (end <= fGapStart)
+			return new String(fContent, offset, length);
+
+		StringBuffer buf = new StringBuffer(length);
+		buf.append(fContent, offset, fGapStart - offset);
+		buf.append(fContent, fGapEnd, end - fGapStart);
+		return buf.toString();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#getLength()
+	 */
+	public final int getLength() {
+		return fContent.length - gapSize();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+	 */
+	public final void set(String text) {
+		/*
+		 * Moves the gap to the end of the content. There is no sensible
+		 * prediction of where the next change will occur, but at least the next
+		 * change will not trigger re-allocation. This is especially important
+		 * when using the GapTextStore within a CopyOnWriteTextStore, where the
+		 * GTS is only initialized right before a modification.
+		 */
+		replace(0, getLength(), text);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ITextStore#replace(int, int,
+	 * java.lang.String)
+	 */
+	public final void replace(int offset, int length, String text) {
+		if (text == null) {
+			adjustGap(offset, length, 0);
+		} else {
+			int textLength = text.length();
+			adjustGap(offset, length, textLength);
+			if (textLength != 0)
+				text.getChars(0, textLength, fContent, offset);
+		}
+	}
+
+	/**
+	 * Moves the gap to <code>offset + add</code>, moving any content after
+	 * <code>offset + remove</code> behind the gap. The gap size is kept between
+	 * 0 and {@link #fThreshold}, leading to re-allocation if needed. The
+	 * content between <code>offset</code> and <code>offset + add</code> is
+	 * undefined after this operation.
+	 * 
+	 * @param offset
+	 *            the offset at which a change happens
+	 * @param remove
+	 *            the number of character which are removed or overwritten at
+	 *            <code>offset</code>
+	 * @param add
+	 *            the number of character which are inserted or overwriting at
+	 *            <code>offset</code>
+	 */
+	private void adjustGap(int offset, int remove, int add) {
+		final int oldGapSize = gapSize();
+		final int newGapSize = oldGapSize - add + remove;
+		final boolean reuseArray = 0 <= newGapSize && newGapSize <= fThreshold;
+
+		final int newGapStart = offset + add;
+		final int newGapEnd;
+
+		if (reuseArray)
+			newGapEnd = moveGap(offset, remove, oldGapSize, newGapSize, newGapStart);
+		else
+			newGapEnd = reallocate(offset, remove, oldGapSize, newGapSize, newGapStart);
+
+		fGapStart = newGapStart;
+		fGapEnd = newGapEnd;
+	}
+
+	/**
+	 * Moves the gap to <code>newGapStart</code>.
+	 * 
+	 * @param offset
+	 *            the change offset
+	 * @param remove
+	 *            the number of removed / overwritten characters
+	 * @param oldGapSize
+	 *            the old gap size
+	 * @param newGapSize
+	 *            the gap size after the change
+	 * @param newGapStart
+	 *            the offset in the array to move the gap to
+	 * @return the new gap end
+	 * @since 3.3
+	 */
+	private int moveGap(int offset, int remove, int oldGapSize, int newGapSize, int newGapStart) {
+		/*
+		 * No re-allocation necessary. The area between the change offset and
+		 * gap can be copied in at most one operation. Don't copy parts that
+		 * will be overwritten anyway.
+		 */
+		final int newGapEnd = newGapStart + newGapSize;
+		if (offset < fGapStart) {
+			int afterRemove = offset + remove;
+			if (afterRemove < fGapStart) {
+				final int betweenSize = fGapStart - afterRemove;
+				arrayCopy(afterRemove, fContent, newGapEnd, betweenSize);
+			}
+			// otherwise, only the gap gets enlarged
+		} else {
+			final int offsetShifted = offset + oldGapSize;
+			final int betweenSize = offsetShifted - fGapEnd; // in the typing
+																// case,
+																// betweenSize
+																// is 0
+			arrayCopy(fGapEnd, fContent, fGapStart, betweenSize);
+		}
+		return newGapEnd;
+	}
+
+	/**
+	 * Reallocates a new array and copies the data from the previous one.
+	 * 
+	 * @param offset
+	 *            the change offset
+	 * @param remove
+	 *            the number of removed / overwritten characters
+	 * @param oldGapSize
+	 *            the old gap size
+	 * @param newGapSize
+	 *            the gap size after the change if no re-allocation would occur
+	 *            (can be negative)
+	 * @param newGapStart
+	 *            the offset in the array to move the gap to
+	 * @return the new gap end
+	 * @since 3.3
+	 */
+	private int reallocate(int offset, int remove, final int oldGapSize, int newGapSize, final int newGapStart) {
+		// the new content length (without any gap)
+		final int newLength = fContent.length - newGapSize;
+		// the new array size based on the gap factor
+		int newArraySize = (int) (newLength * fSizeMultiplier);
+		newGapSize = newArraySize - newLength;
+
+		// bound the gap size within min/max
+		if (newGapSize < fMinGapSize) {
+			newGapSize = fMinGapSize;
+			newArraySize = newLength + newGapSize;
+		} else if (newGapSize > fMaxGapSize) {
+			newGapSize = fMaxGapSize;
+			newArraySize = newLength + newGapSize;
+		}
+
+		// the upper threshold is always twice the gapsize
+		fThreshold = newGapSize * 2;
+		final char[] newContent = allocate(newArraySize);
+		final int newGapEnd = newGapStart + newGapSize;
+
+		/*
+		 * Re-allocation: The old content can be copied in at most 3 operations
+		 * to the newly allocated array. Either one of change offset and the gap
+		 * may come first. - unchanged area before the change offset / gap -
+		 * area between the change offset and the gap (either one may be first)
+		 * - rest area after the change offset / after the gap
+		 */
+		if (offset < fGapStart) {
+			// change comes before gap
+			arrayCopy(0, newContent, 0, offset);
+			int afterRemove = offset + remove;
+			if (afterRemove < fGapStart) {
+				// removal is completely before the gap
+				final int betweenSize = fGapStart - afterRemove;
+				arrayCopy(afterRemove, newContent, newGapEnd, betweenSize);
+				final int restSize = fContent.length - fGapEnd;
+				arrayCopy(fGapEnd, newContent, newGapEnd + betweenSize, restSize);
+			} else {
+				// removal encompasses the gap
+				afterRemove += oldGapSize;
+				final int restSize = fContent.length - afterRemove;
+				arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+			}
+		} else {
+			// gap comes before change
+			arrayCopy(0, newContent, 0, fGapStart);
+			final int offsetShifted = offset + oldGapSize;
+			final int betweenSize = offsetShifted - fGapEnd;
+			arrayCopy(fGapEnd, newContent, fGapStart, betweenSize);
+			final int afterRemove = offsetShifted + remove;
+			final int restSize = fContent.length - afterRemove;
+			arrayCopy(afterRemove, newContent, newGapEnd, restSize);
+		}
+
+		fContent = newContent;
+		return newGapEnd;
+	}
+
+	/**
+	 * Allocates a new <code>char[size]</code>.
+	 * 
+	 * @param size
+	 *            the length of the new array.
+	 * @return a newly allocated char array
+	 * @since 3.3
+	 */
+	private char[] allocate(int size) {
+		return new char[size];
+	}
+
+	/*
+	 * Executes System.arraycopy if length != 0. A length < 0 cannot happen ->
+	 * don't hide coding errors by checking for negative lengths.
+	 * @since 3.3
+	 */
+	private void arrayCopy(int srcPos, char[] dest, int destPos, int length) {
+		if (length != 0)
+			System.arraycopy(fContent, srcPos, dest, destPos, length);
+	}
+
+	/**
+	 * Returns the gap size.
+	 * 
+	 * @return the gap size
+	 * @since 3.3
+	 */
+	private int gapSize() {
+		return fGapEnd - fGapStart;
+	}
+
+	/**
+	 * Returns a copy of the content of this text store. For internal use only.
+	 * 
+	 * @return a copy of the content of this text store
+	 */
+	protected String getContentAsString() {
+		return new String(fContent);
+	}
+
+	/**
+	 * Returns the start index of the gap managed by this text store. For
+	 * internal use only.
+	 * 
+	 * @return the start index of the gap managed by this text store
+	 */
+	protected int getGapStartIndex() {
+		return fGapStart;
+	}
+
+	/**
+	 * Returns the end index of the gap managed by this text store. For internal
+	 * use only.
+	 * 
+	 * @return the end index of the gap managed by this text store
+	 */
+	protected int getGapEndIndex() {
+		return fGapEnd;
+	}
+}
\ No newline at end of file

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/GapTextStore.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,21 @@
+package aQute.bnd.properties;
+
+public interface IDocument {
+
+	int getNumberOfLines();
+
+	IRegion getLineInformation(int lineNum) throws BadLocationException;
+
+	String get();
+
+	String get(int offset, int length) throws BadLocationException;
+
+	String getLineDelimiter(int line) throws BadLocationException;
+
+	int getLength();
+
+	void replace(int offset, int length, String data) throws BadLocationException;
+
+	char getChar(int offset) throws BadLocationException;
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IDocument.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,8 @@
+package aQute.bnd.properties;
+
+public interface IRegion {
+
+	int getLength();
+
+	int getOffset();
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/IRegion.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,65 @@
+package aQute.bnd.properties;
+
+/**
+ * Interface for storing and managing text.
+ * <p>
+ * Provides access to the stored text and allows to manipulate it.
+ * </p>
+ * <p>
+ * Clients may implement this interface or use
+ * {@link org.eclipse.jface.text.GapTextStore} or
+ * {@link org.eclipse.jface.text.CopyOnWriteTextStore}.
+ * </p>
+ */
+public interface ITextStore {
+
+	/**
+	 * Returns the character at the specified offset.
+	 * 
+	 * @param offset
+	 *            the offset in this text store
+	 * @return the character at this offset
+	 */
+	char get(int offset);
+
+	/**
+	 * Returns the text of the specified character range.
+	 * 
+	 * @param offset
+	 *            the offset of the range
+	 * @param length
+	 *            the length of the range
+	 * @return the text of the range
+	 */
+	String get(int offset, int length);
+
+	/**
+	 * Returns number of characters stored in this text store.
+	 * 
+	 * @return the number of characters stored in this text store
+	 */
+	int getLength();
+
+	/**
+	 * Replaces the specified character range with the given text.
+	 * <code>replace(getLength(), 0, "some text")</code> is a valid call and
+	 * appends text to the end of the text store.
+	 * 
+	 * @param offset
+	 *            the offset of the range to be replaced
+	 * @param length
+	 *            the number of characters to be replaced
+	 * @param text
+	 *            the substitution text
+	 */
+	void replace(int offset, int length, String text);
+
+	/**
+	 * Replace the content of the text store with the given text. Convenience
+	 * method for <code>replace(0, getLength(), text</code>.
+	 * 
+	 * @param text
+	 *            the new content of the text store
+	 */
+	void set(String text);
+}
\ No newline at end of file

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/ITextStore.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,60 @@
+package aQute.bnd.properties;
+
+/**
+ * Describes a line as a particular number of characters beginning at a
+ * particular offset, consisting of a particular number of characters, and being
+ * closed with a particular line delimiter.
+ */
+final class Line implements IRegion {
+
+	/** The offset of the line */
+	public int			offset;
+	/** The length of the line */
+	public int			length;
+	/** The delimiter of this line */
+	public final String	delimiter;
+
+	/**
+	 * Creates a new Line.
+	 * 
+	 * @param offset
+	 *            the offset of the line
+	 * @param end
+	 *            the last including character offset of the line
+	 * @param delimiter
+	 *            the line's delimiter
+	 */
+	public Line(int offset, int end, String delimiter) {
+		this.offset = offset;
+		this.length = (end - offset) + 1;
+		this.delimiter = delimiter;
+	}
+
+	/**
+	 * Creates a new Line.
+	 * 
+	 * @param offset
+	 *            the offset of the line
+	 * @param length
+	 *            the length of the line
+	 */
+	public Line(int offset, int length) {
+		this.offset = offset;
+		this.length = length;
+		this.delimiter = null;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IRegion#getOffset()
+	 */
+	public int getOffset() {
+		return offset;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.IRegion#getLength()
+	 */
+	public int getLength() {
+		return length;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Line.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,382 @@
+package aQute.bnd.properties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import aQute.bnd.properties.Document.DelimiterInfo;
+
+public class LineTracker {
+
+	/** The line information */
+	private final List<Line>	fLines	= new ArrayList<Line>();
+	/** The length of the tracked text */
+	private int					fTextLength;
+
+	/**
+	 * Creates a new line tracker.
+	 */
+	protected LineTracker() {}
+
+	/**
+	 * Binary search for the line at a given offset.
+	 * 
+	 * @param offset
+	 *            the offset whose line should be found
+	 * @return the line of the offset
+	 */
+	private int findLine(int offset) {
+
+		if (fLines.size() == 0)
+			return -1;
+
+		int left = 0;
+		int right = fLines.size() - 1;
+		int mid = 0;
+		Line line = null;
+
+		while (left < right) {
+
+			mid = (left + right) / 2;
+
+			line = fLines.get(mid);
+			if (offset < line.offset) {
+				if (left == mid)
+					right = left;
+				else
+					right = mid - 1;
+			} else if (offset > line.offset) {
+				if (right == mid)
+					left = right;
+				else
+					left = mid + 1;
+			} else if (offset == line.offset) {
+				left = right = mid;
+			}
+		}
+
+		line = fLines.get(left);
+		if (line.offset > offset)
+			--left;
+		return left;
+	}
+
+	/**
+	 * Returns the number of lines covered by the specified text range.
+	 * 
+	 * @param startLine
+	 *            the line where the text range starts
+	 * @param offset
+	 *            the start offset of the text range
+	 * @param length
+	 *            the length of the text range
+	 * @return the number of lines covered by this text range
+	 * @exception BadLocationException
+	 *                if range is undefined in this tracker
+	 */
+	private int getNumberOfLines(int startLine, int offset, int length) throws BadLocationException {
+
+		if (length == 0)
+			return 1;
+
+		int target = offset + length;
+
+		Line l = fLines.get(startLine);
+
+		if (l.delimiter == null)
+			return 1;
+
+		if (l.offset + l.length > target)
+			return 1;
+
+		if (l.offset + l.length == target)
+			return 2;
+
+		return getLineNumberOfOffset(target) - startLine + 1;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineLength(int)
+	 */
+	public final int getLineLength(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0 || lines == line)
+			return 0;
+
+		Line l = fLines.get(line);
+		return l.length;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineNumberOfOffset(int)
+	 */
+	public final int getLineNumberOfOffset(int position) throws BadLocationException {
+		if (position < 0 || position > fTextLength)
+			throw new BadLocationException();
+
+		if (position == fTextLength) {
+
+			int lastLine = fLines.size() - 1;
+			if (lastLine < 0)
+				return 0;
+
+			Line l = fLines.get(lastLine);
+			return (l.delimiter != null ? lastLine + 1 : lastLine);
+		}
+
+		return findLine(position);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineInformationOfOffset(int)
+	 */
+	public final IRegion getLineInformationOfOffset(int position) throws BadLocationException {
+		if (position > fTextLength)
+			throw new BadLocationException();
+
+		if (position == fTextLength) {
+			int size = fLines.size();
+			if (size == 0)
+				return new Region(0, 0);
+			Line l = fLines.get(size - 1);
+			return (l.delimiter != null ? new Line(fTextLength, 0) : new Line(fTextLength - l.length, l.length));
+		}
+
+		return getLineInformation(findLine(position));
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineInformation(int)
+	 */
+	public final IRegion getLineInformation(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return new Line(0, 0);
+
+		if (line == lines) {
+			Line l = fLines.get(line - 1);
+			return new Line(l.offset + l.length, 0);
+		}
+
+		Line l = fLines.get(line);
+		return (l.delimiter != null ? new Line(l.offset, l.length - l.delimiter.length()) : l);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineOffset(int)
+	 */
+	public final int getLineOffset(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return 0;
+
+		if (line == lines) {
+			Line l = fLines.get(line - 1);
+			if (l.delimiter != null)
+				return l.offset + l.length;
+			throw new BadLocationException();
+		}
+
+		Line l = fLines.get(line);
+		return l.offset;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines()
+	 */
+	public final int getNumberOfLines() {
+		int lines = fLines.size();
+
+		if (lines == 0)
+			return 1;
+
+		Line l = fLines.get(lines - 1);
+		return (l.delimiter != null ? lines + 1 : lines);
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getNumberOfLines(int, int)
+	 */
+	public final int getNumberOfLines(int position, int length) throws BadLocationException {
+
+		if (position < 0 || position + length > fTextLength)
+			throw new BadLocationException();
+
+		if (length == 0) // optimization
+			return 1;
+
+		return getNumberOfLines(getLineNumberOfOffset(position), position, length);
+	}
+
+	/*
+	 * @see
+	 * org.eclipse.jface.text.ILineTracker#computeNumberOfLines(java.lang.String
+	 * )
+	 */
+	public final int computeNumberOfLines(String text) {
+		int count = 0;
+		int start = 0;
+		DelimiterInfo delimiterInfo = nextDelimiterInfo(text, start);
+		while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+			++count;
+			start = delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+			delimiterInfo = nextDelimiterInfo(text, start);
+		}
+		return count;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#getLineDelimiter(int)
+	 */
+	public final String getLineDelimiter(int line) throws BadLocationException {
+		int lines = fLines.size();
+
+		if (line < 0 || line > lines)
+			throw new BadLocationException();
+
+		if (lines == 0)
+			return null;
+
+		if (line == lines)
+			return null;
+
+		Line l = fLines.get(line);
+		return l.delimiter;
+	}
+
+	/**
+	 * Returns the information about the first delimiter found in the given text
+	 * starting at the given offset.
+	 * 
+	 * @param text
+	 *            the text to be searched
+	 * @param offset
+	 *            the offset in the given text
+	 * @return the information of the first found delimiter or <code>null</code>
+	 */
+	protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+
+		char ch;
+		int length = text.length();
+		for (int i = offset; i < length; i++) {
+
+			ch = text.charAt(i);
+			if (ch == '\r') {
+
+				if (i + 1 < length) {
+					if (text.charAt(i + 1) == '\n') {
+						DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+						fDelimiterInfo.delimiter = Document.DELIMITERS[2];
+						fDelimiterInfo.delimiterIndex = i;
+						fDelimiterInfo.delimiterLength = 2;
+						return fDelimiterInfo;
+					}
+				}
+				DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+				fDelimiterInfo.delimiter = Document.DELIMITERS[0];
+				fDelimiterInfo.delimiterIndex = i;
+				fDelimiterInfo.delimiterLength = 1;
+				return fDelimiterInfo;
+
+			} else if (ch == '\n') {
+				DelimiterInfo fDelimiterInfo = new DelimiterInfo();
+				fDelimiterInfo.delimiter = Document.DELIMITERS[1];
+				fDelimiterInfo.delimiterIndex = i;
+				fDelimiterInfo.delimiterLength = 1;
+				return fDelimiterInfo;
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Creates the line structure for the given text. Newly created lines are
+	 * inserted into the line structure starting at the given position. Returns
+	 * the number of newly created lines.
+	 * 
+	 * @param text
+	 *            the text for which to create a line structure
+	 * @param insertPosition
+	 *            the position at which the newly created lines are inserted
+	 *            into the tracker's line structure
+	 * @param offset
+	 *            the offset of all newly created lines
+	 * @return the number of newly created lines
+	 */
+	private int createLines(String text, int insertPosition, int offset) {
+
+		int count = 0;
+		int start = 0;
+		DelimiterInfo delimiterInfo = nextDelimiterInfo(text, 0);
+
+		while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+
+			int index = delimiterInfo.delimiterIndex + (delimiterInfo.delimiterLength - 1);
+
+			if (insertPosition + count >= fLines.size())
+				fLines.add(new Line(offset + start, offset + index, delimiterInfo.delimiter));
+			else
+				fLines.add(insertPosition + count, new Line(offset + start, offset + index, delimiterInfo.delimiter));
+
+			++count;
+			start = index + 1;
+			delimiterInfo = nextDelimiterInfo(text, start);
+		}
+
+		if (start < text.length()) {
+			if (insertPosition + count < fLines.size()) {
+				// there is a line below the current
+				Line l = fLines.get(insertPosition + count);
+				int delta = text.length() - start;
+				l.offset -= delta;
+				l.length += delta;
+			} else {
+				fLines.add(new Line(offset + start, offset + text.length() - 1, null));
+				++count;
+			}
+		}
+
+		return count;
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#replace(int, int,
+	 * java.lang.String)
+	 */
+	public final void replace(@SuppressWarnings("unused") int position, @SuppressWarnings("unused") int length, @SuppressWarnings("unused") String text) throws BadLocationException {
+		throw new UnsupportedOperationException();
+	}
+
+	/*
+	 * @see org.eclipse.jface.text.ILineTracker#set(java.lang.String)
+	 */
+	public final void set(String text) {
+		fLines.clear();
+		if (text != null) {
+			fTextLength = text.length();
+			createLines(text, 0, 0);
+		}
+	}
+
+	/**
+	 * Returns the internal data structure, a {@link List} of {@link Line}s.
+	 * Used only by {@link TreeLineTracker#TreeLineTracker(ListLineTracker)}.
+	 * 
+	 * @return the internal list of lines.
+	 */
+	final List<Line> getLines() {
+		return fLines;
+	}
+}
\ No newline at end of file

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineTracker.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,5 @@
+package aQute.bnd.properties;
+
+public enum LineType {
+	blank, comment, entry, eof
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/LineType.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,125 @@
+package aQute.bnd.properties;
+
+import static aQute.bnd.properties.LineType.*;
+
+public class PropertiesLineReader {
+
+	private final IDocument	document;
+	private final int		lineCount;
+
+	private int				lineNum		= 0;
+
+	private IRegion			lastRegion	= null;
+	private String			lastKey		= null;
+	private String			lastValue	= null;
+
+	public PropertiesLineReader(IDocument document) {
+		this.document = document;
+		this.lineCount = document.getNumberOfLines();
+	}
+
+	public PropertiesLineReader(String data) {
+		this(new Document(data));
+	}
+
+	public LineType next() throws Exception {
+		int index = 0;
+		char[] chars = null;
+
+		StringBuilder keyData = new StringBuilder();
+		StringBuilder valueData = new StringBuilder();
+		StringBuilder currentBuffer = keyData;
+
+		boolean started = false;
+
+		mainLoop: while (true) {
+			if (chars == null)
+				chars = grabLine(false);
+			if (chars == null)
+				return eof;
+
+			if (index >= chars.length)
+				break;
+
+			char c = chars[index];
+			if (c == '\\') {
+				index++;
+				if (index == chars.length) {
+					chars = grabLine(true);
+					index = 0;
+					if (chars == null || chars.length == 0)
+						break; // The last line ended with a backslash
+				}
+				currentBuffer.append(chars[index]);
+				index++;
+				continue mainLoop;
+			}
+
+			if (c == '=' || c == ':')
+				currentBuffer = valueData;
+
+			if (!started && (c == '#' || c == '!'))
+				return comment;
+
+			if (Character.isWhitespace(c)) {
+				if (started) {
+					// whitespace ends the key
+					currentBuffer = valueData;
+				}
+			} else {
+				started = true;
+				currentBuffer.append(c);
+			}
+
+			index++;
+		}
+
+		if (!started)
+			return blank;
+
+		lastKey = keyData.toString();
+		return entry;
+	}
+
+	private char[] grabLine(boolean continued) throws BadLocationException {
+		if (lineNum >= lineCount) {
+			lastRegion = null;
+			return null;
+		}
+
+		IRegion lineInfo = document.getLineInformation(lineNum);
+		char[] chars = document.get(lineInfo.getOffset(), lineInfo.getLength()).toCharArray();
+
+		if (continued) {
+			int length = lastRegion.getLength();
+			length += document.getLineDelimiter(lineNum - 1).length();
+			length += lineInfo.getLength();
+			lastRegion = new Region(lastRegion.getOffset(), length);
+		} else {
+			lastRegion = lineInfo;
+		}
+
+		lineNum++;
+		return chars;
+	}
+
+	public IRegion region() {
+		if (lastRegion == null)
+			throw new IllegalStateException("Last region not available: either before start or after end of document.");
+		return lastRegion;
+	}
+
+	public String key() {
+		if (lastKey == null)
+			throw new IllegalStateException(
+					"Last key not available: either before state or after end of document, or last line type was not 'entry'.");
+		return lastKey;
+	}
+
+	public String value() {
+		if (lastValue == null)
+			throw new IllegalStateException(
+					"Last value not available: either before state or after end of document, or last line type was not 'entry'.");
+		return lastValue;
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesLineReader.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,115 @@
+package aQute.bnd.properties;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.io.*;
+
+public class PropertiesReader {
+	static Pattern	PROPERTY	= Pattern.compile("(\\s*#.*$)|(([^\\s]+)\\s*[:=]?\\s*([^#])(#.*)$)|\\s+([^#]*)(#.*)$)",
+										Pattern.MULTILINE);
+
+	public static Properties read(Properties p, File f) throws Exception {
+		return read(p, IO.reader(f));
+	}
+
+	public static Properties read(Properties p, InputStream in, String charset) throws IOException {
+		return read(p, IO.reader(in, charset));
+	}
+
+	public static Properties read(Properties p, InputStream in) throws IOException {
+		return read(p, IO.reader(in));
+	}
+
+	public static Properties read(Properties p, URL in) throws IOException {
+		return read(p, IO.reader(in.openStream()));
+	}
+
+	private static Properties read(Properties p, BufferedReader reader) throws IOException {
+		if (p != null)
+			p = new Properties();
+
+		String line = reader.readLine();
+		String key = null;
+		StringBuilder value = new StringBuilder();
+
+		while (line != null) {
+			Matcher m = PROPERTY.matcher(line);
+			if (m.matches()) {
+
+				if (m.group(1) != null)
+					continue; // comment line
+
+				if (m.group(2) != null) {
+					// header
+					if (key != null) {
+						cleanup(value);
+						p.put(key.toString(), value.toString());
+						key = null;
+						value.delete(0, value.length());
+					}
+					key = m.group(3);
+					value.append(m.group(4));
+				} else {
+					value.append(m.group(6));
+				}
+			} else {
+				System.out.println("Assume empty: " + line);
+			}
+			line = reader.readLine();
+		}
+		if (key != null) {
+			cleanup(value);
+			p.put(key.toString(), value.toString());
+		}
+		return p;
+	}
+
+	private static void cleanup(StringBuilder value) {
+		for (int i = 0; i < value.length(); i++) {
+			if (value.charAt(i) == '\\') {
+				value.deleteCharAt(i);
+				if (i < value.length()) {
+					char c = value.charAt(i);
+					switch (c) {
+						case 't' :
+							value.setCharAt(i, '\t');
+							break;
+						case 'r' :
+							value.setCharAt(i, '\r');
+							break;
+						case 'n' :
+							value.setCharAt(i, '\n');
+							break;
+						case 'f' :
+							value.setCharAt(i, '\f');
+							break;
+						case 'b' :
+							value.setCharAt(i, '\b');
+							break;
+
+						case 'u' :
+							if (i + 5 >= value.length())
+								throw new IllegalArgumentException("Invalid unicode escape " + value.substring(i));
+
+							String n = value.substring(i + 1, i + 5);
+							try {
+								int code = Integer.valueOf(n, 16);
+								value.delete(i + 1, i + 5);
+								value.setCharAt(i, (char) code);
+							}
+							catch (Exception e) {
+								throw new IllegalArgumentException("Invalid unicode escape " + value.substring(i));
+							}
+							break;
+						default :
+							throw new IllegalArgumentException("Invalid  escape " + value);
+					}
+				}
+
+			}
+		}
+	}
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/PropertiesReader.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java Mon Jul 16 13:43:38 2012
@@ -0,0 +1,22 @@
+package aQute.bnd.properties;
+
+public class Region implements IRegion {
+
+	private final int	offset;
+	private final int	length;
+
+	public Region(int offset, int length) {
+		this.offset = offset;
+		this.length = length;
+	}
+
+	public int getOffset() {
+		return offset;
+
+	}
+
+	public int getLength() {
+		return length;
+	}
+
+}

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/Region.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Added: felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/packageinfo?rev=1362033&view=auto
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/packageinfo (added)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/properties/packageinfo Mon Jul 16 13:43:38 2012
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java Mon Jul 16 13:43:38 2012
@@ -5,12 +5,11 @@ import java.util.*;
 import java.util.Map.Entry;
 import java.util.jar.*;
 
+import aQute.bnd.header.*;
+import aQute.bnd.osgi.*;
 import aQute.bnd.service.*;
 import aQute.lib.io.*;
-import aQute.lib.osgi.*;
 import aQute.libg.generics.*;
-import aQute.libg.header.*;
-import aQute.libg.version.*;
 import aQute.service.reporter.*;
 
 public class EclipseRepo implements Plugin, RepositoryPlugin {

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/AnalyzerPlugin.java Mon Jul 16 13:43:38 2012
@@ -1,6 +1,6 @@
 package aQute.bnd.service;
 
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 public interface AnalyzerPlugin {
 

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Deploy.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Deploy.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Deploy.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/Deploy.java Mon Jul 16 13:43:38 2012
@@ -1,7 +1,7 @@
 package aQute.bnd.service;
 
 import aQute.bnd.build.*;
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 /**
  * Deploy this artifact to maven.

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/MakePlugin.java Mon Jul 16 13:43:38 2012
@@ -2,7 +2,7 @@ package aQute.bnd.service;
 
 import java.util.*;
 
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 public interface MakePlugin {
 

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryListenerPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryListenerPlugin.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryListenerPlugin.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryListenerPlugin.java Mon Jul 16 13:43:38 2012
@@ -2,7 +2,7 @@ package aQute.bnd.service;
 
 import java.io.*;
 
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 public interface RepositoryListenerPlugin {
 

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/RepositoryPlugin.java Mon Jul 16 13:43:38 2012
@@ -3,8 +3,7 @@ package aQute.bnd.service;
 import java.io.*;
 import java.util.*;
 
-import aQute.lib.osgi.*;
-import aQute.libg.version.*;
+import aQute.bnd.osgi.*;
 
 public interface RepositoryPlugin {
 	public enum Strategy {

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/SignerPlugin.java Mon Jul 16 13:43:38 2012
@@ -1,6 +1,6 @@
 package aQute.bnd.service;
 
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 public interface SignerPlugin {
 	/**

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Differ.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Differ.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Differ.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Differ.java Mon Jul 16 13:43:38 2012
@@ -1,6 +1,6 @@
 package aQute.bnd.service.diff;
 
-import aQute.lib.osgi.*;
+import aQute.bnd.osgi.*;
 
 /**
  * Compare two Jars and report the differences.

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Type.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Type.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Type.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/service/diff/Type.java Mon Jul 16 13:43:38 2012
@@ -1,7 +1,7 @@
 package aQute.bnd.service.diff;
 
 public enum Type {
-	ACCESS, BUNDLE, API, MANIFEST, PACKAGE, CLASS, INTERFACE, ANNOTATION, ENUM, EXTENDS, IMPLEMENTS, FIELD, METHOD, ANNOTATED, PROPERTY, RESOURCE, CUSTOM, CLAUSE, HEADER, PARAMETER, CLASS_VERSION, RESOURCES, CONSTANT, RETURN, VERSION, DEPRECATED, REPO, PROGRAM, REVISION;
+	ACCESS, BUNDLE, API, MANIFEST, PACKAGE, CLASS, INTERFACE, ANNOTATION, ENUM, EXTENDS, IMPLEMENTS, FIELD, METHOD, ANNOTATED, PROPERTY, RESOURCE, SHA, CUSTOM, CLAUSE, HEADER, PARAMETER, CLASS_VERSION, RESOURCES, CONSTANT, RETURN, VERSION, DEPRECATED, REPO, PROGRAM, REVISION;
 
 	public boolean isInherited() {
 		// TODO Auto-generated method stub

Modified: felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java?rev=1362033&r1=1362032&r2=1362033&view=diff
==============================================================================
--- felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java (original)
+++ felix/trunk/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java Mon Jul 16 13:43:38 2012
@@ -5,8 +5,8 @@ import java.util.*;
 import java.util.Map.Entry;
 import java.util.concurrent.*;
 
+import aQute.bnd.osgi.*;
 import aQute.bnd.service.*;
-import aQute.lib.osgi.*;
 import aQute.libg.command.*;
 import aQute.service.reporter.*;