Latest bnd code

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1355520 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
index 50619c8..04d14fc 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/Workspace.java
@@ -15,7 +15,7 @@
 import aQute.lib.deployer.*;
 import aQute.lib.io.*;
 import aQute.lib.osgi.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Workspace extends Processor {
 	public static final String					BUILDFILE	= "build.bnd";
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
index 9a1753c..64c0078 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
@@ -1,19 +1,12 @@
 package aQute.bnd.build;
 
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
 
-import aQute.bnd.service.RepositoryPlugin;
-import aQute.lib.osgi.Jar;
-import aQute.libg.version.Version;
-import aQute.libg.version.VersionRange;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
 
 public class WorkspaceRepository implements RepositoryPlugin {
 	private final Workspace	workspace;
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
index 8dee7e2..4450c39 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/BndEditModel.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model;
 
 import java.beans.*;
@@ -15,14 +5,13 @@
 import java.util.*;
 import java.util.Map.Entry;
 
-import org.osgi.framework.*;
-
 import aQute.bnd.build.model.clauses.*;
 import aQute.bnd.build.model.conversions.*;
+import aQute.lib.osgi.*;
 import aQute.lib.properties.*;
 import aQute.libg.header.*;
 import aQute.libg.tuple.*;
-import aQute.libg.version.Version;
+import aQute.libg.version.*;
 
 /**
  * A model for a Bnd file. In the first iteration, use a simple Properties
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
index dcdf969..679d874 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/LowerVersionMatchType.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model;
 
 public enum LowerVersionMatchType {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
index 91d97ca..89a4597 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/UpperVersionMatchType.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model;
 
 public enum UpperVersionMatchType {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
index 3367c9f..af1b96f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/VersionPolicy.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model;
 
 public class VersionPolicy {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
index 7c6700e..66272d6 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ComponentSvcReference.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model.clauses;
 
 public class ComponentSvcReference implements Cloneable {
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
index 0e9be04..c55ebe2 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ExportedPackage.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model.clauses;
 
 import org.osgi.framework.Constants;
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
index b0391a6..f17a069 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/HeaderClause.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model.clauses;
 
 import java.util.ArrayList;
@@ -137,6 +127,7 @@
 		return this.name.compareTo(other.name);
 	}
 
+	@SuppressWarnings("deprecation")
 	@Override
 	public int hashCode() {
 		final int prime = 31;
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
index 5e0854c..caaea09 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ImportPattern.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model.clauses;
 
 import org.osgi.framework.Constants;
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
index 69f8610..dcbd8f5 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/model/clauses/ServiceComponent.java
@@ -1,13 +1,3 @@
-/*******************************************************************************
- * Copyright (c) 2010 Neil Bartlett.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- *     Neil Bartlett - initial API and implementation
- *******************************************************************************/
 package aQute.bnd.build.model.clauses;
 
 import java.util.ArrayList;
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
index f387048..70abede 100644
--- a/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2009, 2010). 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.bnd.compatibility;
 
 import java.lang.reflect.*;
diff --git a/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java b/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
index 3228b7b..fd27e41 100644
--- a/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
+++ b/bundleplugin/src/main/java/aQute/bnd/differ/Baseline.java
@@ -9,8 +9,8 @@
 import aQute.lib.osgi.*;
 import aQute.libg.generics.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 /**
  * This class maintains
diff --git a/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java b/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java
index 7c776b3..614071f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java
+++ b/bundleplugin/src/main/java/aQute/bnd/make/component/ComponentAnnotationReader.java
@@ -10,7 +10,7 @@
 import aQute.lib.osgi.*;
 import aQute.lib.osgi.Clazz.MethodDef;
 import aQute.lib.osgi.Descriptors.TypeRef;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class ComponentAnnotationReader extends ClassDataCollector {
 	String						EMPTY[]					= new String[0];
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
index b7a1c12..4cc4cf3 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
@@ -9,7 +9,7 @@
 import aQute.lib.osgi.*;
 import aQute.libg.command.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class MavenDeploy implements Deploy, Plugin {
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
index 29de79d..da4eff0 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
@@ -8,7 +8,7 @@
 import aQute.lib.osgi.*;
 import aQute.libg.command.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class MavenDeployCmd extends Processor {
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java
index d74b6d6..7be8c9e 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java
@@ -3,7 +3,7 @@
 import java.util.*;
 
 import aQute.bnd.service.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class MavenGroup implements BsnToMavenPath, Plugin {
 	String	groupId	= "";
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
index 00e7863..06f279f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
@@ -6,8 +6,8 @@
 
 import aQute.bnd.service.*;
 import aQute.lib.osgi.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class MavenRepository implements RepositoryPlugin, Plugin, BsnToMavenPath {
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
index 17bf92f..26ba26f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
@@ -7,8 +7,8 @@
 import aQute.bnd.service.*;
 import aQute.lib.io.*;
 import aQute.lib.osgi.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class MavenRemoteRepository implements RepositoryPlugin, RegistryPlugin, Plugin {
 	Reporter	reporter;
diff --git a/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java b/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
index 816dd1b..e52ef24 100644
--- a/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
+++ b/bundleplugin/src/main/java/aQute/bnd/repo/eclipse/EclipseRepo.java
@@ -10,8 +10,8 @@
 import aQute.lib.osgi.*;
 import aQute.libg.generics.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class EclipseRepo implements Plugin, RepositoryPlugin {
 	File						root;
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java b/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
index f697308..31db491 100644
--- a/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
+++ b/bundleplugin/src/main/java/aQute/bnd/service/BndListener.java
@@ -3,7 +3,7 @@
 import java.io.*;
 import java.util.concurrent.atomic.*;
 
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class BndListener {
 	final AtomicInteger	inside	= new AtomicInteger();
diff --git a/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java b/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java
index 6b5bfcf..15b7898 100644
--- a/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java
+++ b/bundleplugin/src/main/java/aQute/bnd/service/Plugin.java
@@ -2,7 +2,7 @@
 
 import java.util.*;
 
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * An optional interface for plugins. If a plugin implements this interface then
diff --git a/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java b/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
index b96bd41..346491f 100644
--- a/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
+++ b/bundleplugin/src/main/java/aQute/bnd/signing/JartoolSigner.java
@@ -8,7 +8,7 @@
 import aQute.bnd.service.*;
 import aQute.lib.osgi.*;
 import aQute.libg.command.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * Sign the jar file. -sign : <alias> [ ';' 'password:=' <password> ] [ ';'
diff --git a/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java b/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
index 40c80ca..4943d92 100644
--- a/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
+++ b/bundleplugin/src/main/java/aQute/lib/collections/ExtList.java
@@ -12,6 +12,22 @@
 		}
 	}
 
+	public ExtList(int size) {
+		super(size);
+	}
+
+	public static ExtList<String> from(String s) {
+		// TODO make sure no \ before comma
+		return from(s, "\\s*,\\s*");
+	}
+	public static ExtList<String> from(String s, String delimeter) {
+		ExtList<String> result = new ExtList<String>();
+		String[] parts = s.split(delimeter);
+		for (String p : parts)
+			result.add(p);
+		return result;
+	}
+
 	public String join() {
 		return join(",");
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
index f22f840..621b558 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
@@ -32,14 +32,18 @@
 	}
 
 	public <T> T convert(TypeReference<T> type, Object o) throws Exception {
-		return (T) convert( type.getType(), o);
+		return (T) convert(type.getType(), o);
 	}
-	
-	public Object convert(Type type, Object o) throws Exception {
-		if (o == null)
-			return null; // compatible with any
 
-		
+	public Object convert(Type type, Object o) throws Exception {
+		Class resultType = getRawClass(type);
+		if (o == null) {
+			if (resultType.isPrimitive()||  Number.class.isAssignableFrom(resultType)) 
+				return convert(type,0);
+
+			return null; // compatible with any
+		}
+
 		Hook hook = hooks.get(type);
 		if (hook != null) {
 			Object value = hook.convert(type, o);
@@ -47,7 +51,6 @@
 				return value;
 		}
 
-		Class resultType = getRawClass(type);
 		Class< ? > actualType = o.getClass();
 
 		// We can always make a string
@@ -111,6 +114,10 @@
 		if (resultType.isAssignableFrom(o.getClass()))
 			return o;
 
+		if (Map.class.isAssignableFrom(actualType) && resultType.isInterface()) {
+			return proxy(resultType, (Map) o);
+		}
+
 		// Simple type coercion
 
 		if (resultType == boolean.class || resultType == Boolean.class) {
@@ -243,6 +250,8 @@
 		return error("No conversion found for " + o.getClass() + " to " + type);
 	}
 
+
+
 	private Number number(Object o) {
 		if (o instanceof Number)
 			return (Number) o;
@@ -311,8 +320,9 @@
 				result = new TreeMap();
 			else if (rawClass.isAssignableFrom(ConcurrentHashMap.class))
 				result = new ConcurrentHashMap();
-			else
+			else {
 				return (Map) error("Cannot find suitable map for map interface " + rawClass);
+			}
 		} else
 			result = rawClass.newInstance();
 
@@ -421,4 +431,44 @@
 		this.hooks.put(type, hook);
 		return this;
 	}
+
+	/**
+	 * Convert a map to an interface.
+	 * 
+	 * @param interfc
+	 * @param properties
+	 * @return
+	 */
+	public <T> T proxy(Class<T> interfc, final Map< ? , ? > properties) {
+		return (T) Proxy.newProxyInstance(interfc.getClassLoader(), new Class[] {
+			interfc
+		}, new InvocationHandler() {
+
+			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+				Object o = properties.get(method.getName());
+				if ( o == null)
+					o = properties.get(mangleMethodName(method.getName()));
+
+				return convert( method.getGenericReturnType(), o);
+			}
+
+		});
+	}
+
+	public static String mangleMethodName(String id) {
+		StringBuilder sb = new StringBuilder(id);
+		for (int i = 0; i < sb.length(); i++) {
+			char c = sb.charAt(i);
+			boolean twice = i < sb.length() - 1 && sb.charAt(i + 1) == c;
+			if (c == '$' || c == '_') {
+				if (twice)
+					sb.deleteCharAt(i + 1);
+				else if (c == '$')
+					sb.deleteCharAt(i--); // Remove dollars
+				else
+					sb.setCharAt(i, '.'); // Make _ into .
+			}
+		}
+		return sb.toString();
+	}
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java b/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
index 2b52308..3271227 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/TypeReference.java
@@ -4,6 +4,9 @@
 
 public class TypeReference<T> implements Type {
 
+	protected TypeReference() {
+		// Make sure it cannot be directly instantiated
+	}
 	public Type getType() {
 		return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
 	}
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
index 4d0f5b9..9b01ff7 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileInstallRepo.java
@@ -7,8 +7,8 @@
 
 import aQute.lib.osgi.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class FileInstallRepo extends FileRepo {
 
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
index 4be5ec8..3703b8b 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
@@ -9,8 +9,8 @@
 import aQute.lib.io.*;
 import aQute.lib.osgi.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 public class FileRepo implements Plugin, RepositoryPlugin, Refreshable, RegistryPlugin {
 	public final static String	LOCATION	= "location";
@@ -157,10 +157,10 @@
 		reporter.trace("updating %s ", file.getAbsolutePath());
 		if (!file.exists() || file.lastModified() < jar.lastModified()) {
 			jar.write(file);
-			reporter.progress("updated " + file.getAbsolutePath());
+			reporter.progress(-1, "updated " + file.getAbsolutePath());
 			fireBundleAdded(jar, file);
 		} else {
-			reporter.progress("Did not update " + jar + " because repo has a newer version");
+			reporter.progress(-1, "Did not update " + jar + " because repo has a newer version");
 			reporter.trace("NOT Updating " + fName + " (repo is newer)");
 		}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
index c36727f..e1b7560 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLine.java
@@ -9,6 +9,7 @@
 import aQute.lib.justif.*;
 import aQute.libg.generics.*;
 import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * Helps parsing command lines. This class takes target object, a primary
@@ -101,7 +102,7 @@
 
 				// Handle vararg
 
-				if (pattern.equals("...")) {
+				if (pattern.contains("...")) {
 					i = Integer.MAX_VALUE;
 					break;
 				}
diff --git a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
index afeb0b9..4f2cce3 100644
--- a/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
+++ b/bundleplugin/src/main/java/aQute/lib/getopt/CommandLineMessages.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.lib.getopt;
 
 import java.util.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java b/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
index 032f08f..8e608f8 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/JSONCodec.java
@@ -190,6 +190,8 @@
 						h = new CollectionHandler(rawClass, pt.getActualTypeArguments()[0]);
 					else if (Map.class.isAssignableFrom(rawClass))
 						h = new MapHandler(rawClass, pt.getActualTypeArguments()[0], pt.getActualTypeArguments()[1]);
+					else if (Dictionary.class.isAssignableFrom(rawClass))
+						h = new MapHandler(Hashtable.class, pt.getActualTypeArguments()[0], pt.getActualTypeArguments()[1]);
 					else
 						throw new IllegalArgumentException("Found a parameterized type that is not a map or collection");
 				}
diff --git a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
index 349737a..62bf07f 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/MapHandler.java
@@ -21,6 +21,8 @@
 				rawClass = Hashtable.class;
 			else if (rawClass.isAssignableFrom(LinkedHashMap.class))
 				rawClass = LinkedHashMap.class;
+			else if (rawClass.isAssignableFrom(Dictionary.class))
+				rawClass = Hashtable.class;
 			else
 				throw new IllegalArgumentException("Unknown map interface: " + rawClass);
 		}
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java b/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
index 3efea0a..a91a0f0 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/AnalyzerMessages.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.lib.osgi;
 
 import aQute.libg.reporter.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
index f49d6e3..b31be5c 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Builder.java
@@ -117,7 +117,6 @@
 		dot.setName(getBsn());
 
 		sign(dot);
-		doDigests(dot);
 		doSaveManifest(dot);
 
 		doDiff(dot); // check if need to diff this bundle
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java b/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
index d76b695..3886c48 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/CombinedResource.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.lib.osgi;
 
 import java.io.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
index eefc1e2..47ba4ac 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/CommandResource.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.lib.osgi;
 
 import java.io.*;
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java b/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
index f520378..e705534 100644
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Domain.java
@@ -6,8 +6,8 @@
 import java.util.jar.*;
 
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
 import aQute.libg.version.*;
+import aQute.service.reporter.*;
 
 /**
  * This class abstracts domains that have properties holding OSGi meta data. It
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
index 76a4b0d..725b065 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java
@@ -11,7 +11,7 @@
 
 import aQute.lib.base64.*;
 import aQute.lib.io.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Jar implements Closeable {
 	public enum Compression {
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
index 608e9a0..c6b5dd9 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/Processor.java
@@ -13,7 +13,7 @@
 import aQute.lib.io.*;
 import aQute.libg.generics.*;
 import aQute.libg.header.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Processor extends Domain implements Reporter, Registry, Constants, Closeable {
 
@@ -113,41 +113,64 @@
 		return p;
 	}
 
-	public void warning(String string, Object... args) {
+	public SetLocation warning(String string, Object... args) {
 		Processor p = current();
 		String s = formatArrays(string, args);
 		if (!p.warnings.contains(s))
 			p.warnings.add(s);
 		p.signal();
+		return location(s);
 	}
 
-	public void error(String string, Object... args) {
+	public SetLocation error(String string, Object... args) {
 		Processor p = current();
-		if (p.isFailOk())
-			p.warning(string, args);
-		else {
-			String s = formatArrays(string, args == null ? new Object[0] : args);
-			if (!p.errors.contains(s))
-				p.errors.add(s);
+		try {
+			if (p.isFailOk())
+				return p.warning(string, args);
+			else {
+				String s = formatArrays(string, args == null ? new Object[0] : args);
+				if (!p.errors.contains(s))
+					p.errors.add(s);
+				return location(s);
+			}
 		}
-		p.signal();
+		finally {
+			p.signal();
+		}
 	}
 
-	public void error(String string, Throwable t, Object... args) {
+	public void progress(float progress, String format, Object... args) {
+		format = String.format("[%2d] %s", (int)progress, format);
+		trace(format, args);
+	}
+
+	public void progress(String format, Object... args) {
+		progress(-1f, format, args);
+	}
+
+	public SetLocation exception(Throwable t, String format, Object... args) {
+		return error(format, t, args);
+	}
+
+	public SetLocation error(String string, Throwable t, Object... args) {
 		Processor p = current();
-
-		if (p.isFailOk())
-			p.warning(string + ": " + t, args);
-		else {
-			p.errors.add("Exception: " + t.getMessage());
-			String s = formatArrays(string, args == null ? new Object[0] : args);
-			if (!p.errors.contains(s))
-				p.errors.add(s);
+		try {
+			if (p.exceptions)
+				t.printStackTrace();
+			if (p.isFailOk()) {
+				return p.warning(string + ": " + t, args);
+			}
+			else {
+				p.errors.add("Exception: " + t.getMessage());
+				String s = formatArrays(string, args == null ? new Object[0] : args);
+				if (!p.errors.contains(s))
+					p.errors.add(s);
+				return location(s);
+			}
 		}
-		if (p.exceptions)
-			t.printStackTrace();
-
-		p.signal();
+		finally {
+			p.signal();
+		}
 	}
 
 	public void signal() {}
@@ -184,10 +207,6 @@
 		toBeClosed.remove(jar);
 	}
 
-	public void progress(String s, Object... args) {
-		trace(s, args);
-	}
-
 	public boolean isPedantic() {
 		return current().pedantic;
 	}
@@ -1584,4 +1603,64 @@
 
 		return s + newExtension;
 	}
+
+	/**
+	 * Create a location object and add it to the locations
+	 * 
+	 * @param s
+	 * @return
+	 */
+	List<Location>	locations	= new ArrayList<Location>();
+
+	static class SetLocationImpl extends Location implements SetLocation {
+		public SetLocationImpl(String s) {
+			this.message = s;
+		}
+
+		public SetLocation file(String file) {
+			this.file = file;
+			return this;
+		}
+
+		public SetLocation header(String header) {
+			this.header = header;
+			return this;
+		}
+
+		public SetLocation context(String context) {
+			this.context = context;
+			return this;
+		}
+
+		public SetLocation method(String methodName) {
+			this.methodName = methodName;
+			return this;
+		}
+
+		public SetLocation line(int n) {
+			this.line = n;
+			return this;
+		}
+
+		public SetLocation reference(String reference) {
+			this.reference = reference;
+			return this;
+		}
+
+	}
+
+	private SetLocation location(String s) {
+		SetLocationImpl loc = new SetLocationImpl(s);
+		locations.add(loc);
+		return loc;
+	}
+
+	public Location getLocation(String msg) {
+		for (Location l : locations)
+			if ((l.message != null) && l.message.equals(msg))
+				return l;
+
+		return null;
+	}
+
 }
diff --git a/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java b/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
index cb511e7..c146477 100755
--- a/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
+++ b/bundleplugin/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
@@ -9,7 +9,7 @@
 import org.w3c.dom.*;
 import org.xml.sax.*;
 
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 /**
  * Parse the Eclipse project information for the classpath. Unfortunately, it is
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
index 77bb9f6..179ab25 100644
--- a/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
+++ b/bundleplugin/src/main/java/aQute/lib/properties/CopyOnWriteTextStore.java
@@ -29,7 +29,7 @@
 		/**
 		 * Create an empty text store.
 		 */
-		private StringTextStore() {
+		StringTextStore() {
 			super();
 		}
 
@@ -39,7 +39,7 @@
 		 * @param text
 		 *            the initial content
 		 */
-		private StringTextStore(String text) {
+		StringTextStore(String text) {
 			super();
 			set(text);
 		}
diff --git a/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java
new file mode 100644
index 0000000..58426af
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/properties/PropertiesReader.java
@@ -0,0 +1,115 @@
+package aQute.lib.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);
+					}
+				}
+
+			}
+		}
+	}
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/tag/Tag.java b/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
index 03ae971..cf8668e 100755
--- a/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
+++ b/bundleplugin/src/main/java/aQute/lib/tag/Tag.java
@@ -392,7 +392,7 @@
 		}
 		String suri = sn == null ? mapping.getAttribute("xmlns") : mapping.getAttribute("xmlns:" + sn);
 		String turi = tn == null ? child.findRecursiveAttribute("xmlns") : child.findRecursiveAttribute("xmlns:" + tn);
-		return turi == suri || (turi != null && suri != null && turi.equals(suri));
+		return ((turi == null) && (suri == null)) || ((turi != null) && turi.equals(suri));
 	}
 
 	public String getString(String path) {
diff --git a/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
index adf9824..1f29467 100755
--- a/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
+++ b/bundleplugin/src/main/java/aQute/libg/classdump/ClassDumper.java
@@ -1,5 +1,3 @@
-/* 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.*;
diff --git a/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java b/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java
index a2a9699..8bd998d 100755
--- a/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java
+++ b/bundleplugin/src/main/java/aQute/libg/clauses/Selector.java
@@ -1,21 +1,3 @@
-/*
- * $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.*;
diff --git a/bundleplugin/src/main/java/aQute/libg/command/Command.java b/bundleplugin/src/main/java/aQute/libg/command/Command.java
index 6bb24c3..deb06c5 100644
--- a/bundleplugin/src/main/java/aQute/libg/command/Command.java
+++ b/bundleplugin/src/main/java/aQute/libg/command/Command.java
@@ -7,7 +7,7 @@
 import java.util.concurrent.atomic.*;
 
 import aQute.lib.io.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Command {
 
diff --git a/bundleplugin/src/main/java/aQute/libg/glob/Glob.java b/bundleplugin/src/main/java/aQute/libg/glob/Glob.java
index b48c147..6202524 100644
--- a/bundleplugin/src/main/java/aQute/libg/glob/Glob.java
+++ b/bundleplugin/src/main/java/aQute/libg/glob/Glob.java
@@ -1,5 +1,6 @@
 package aQute.libg.glob;
 
+import java.util.*;
 import java.util.regex.*;
 
 public class Glob {
@@ -25,15 +26,6 @@
 		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()) {
@@ -106,4 +98,21 @@
 		}
 		return sb.toString();
 	}
+
+	public void select(List<?> objects) {
+		for ( Iterator<?> i =objects.iterator(); i.hasNext(); ) {
+			String s = i.next().toString();
+			if ( !matcher(s).matches())
+				i.remove();
+		}
+	}
+
+	public static Pattern toPattern(String s) {
+		try {
+			return Pattern.compile( convertGlobToRegEx(s));
+		} catch( Exception e) {
+			// ignore
+		}
+		return null;
+	}
 }
diff --git a/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
index 6be995a..a372527 100755
--- a/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
+++ b/bundleplugin/src/main/java/aQute/libg/header/OSGiHeader.java
@@ -4,7 +4,7 @@
 
 import aQute.libg.generics.*;
 import aQute.libg.qtokens.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class OSGiHeader {
 
diff --git a/bundleplugin/src/main/java/aQute/libg/header/Parameters.java b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
index f2354c4..02e3463 100644
--- a/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
+++ b/bundleplugin/src/main/java/aQute/libg/header/Parameters.java
@@ -3,7 +3,7 @@
 import java.util.*;
 
 import aQute.lib.collections.*;
-import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
 
 public class Parameters implements Map<String,Attrs> {
 	private LinkedHashMap<String,Attrs>	map;
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/Message.java b/bundleplugin/src/main/java/aQute/libg/reporter/Message.java
index 28386c0..afaad88 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/Message.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/Message.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.reporter;
 
 public @interface Message {
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java b/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
index 033df00..c61cfb5 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/Messages.java
@@ -1,26 +1,12 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.reporter;
 
+import aQute.service.reporter.*;
+
 
 public interface Messages {
-	static public class ERROR {}
+	static public interface ERROR extends Reporter.SetLocation {}
 
-	static public class WARNING {}
+	static public interface WARNING extends Reporter.SetLocation {}
 
 	ERROR NoSuchFile_(Object r);
 
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java b/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java
deleted file mode 100755
index 1ad0c31..0000000
--- a/bundleplugin/src/main/java/aQute/libg/reporter/Reporter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-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();
-	
-	boolean isExceptions();
-}
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
index 676fb1b..dfb2c9e 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterAdapter.java
@@ -5,18 +5,61 @@
 import java.util.regex.*;
 
 import aQute.libg.generics.*;
+import aQute.service.reporter.*;
 
 /**
  * Mainly used for testing where reporters are needed.
  */
-public class ReporterAdapter implements Reporter {
+public class ReporterAdapter implements Reporter, Report {
 	final List<String>	errors		= new ArrayList<String>();
 	final List<String>	warnings	= new ArrayList<String>();
+	final List<LocationImpl> locations = new ArrayList<LocationImpl>();
+	
+	static class LocationImpl extends Location implements SetLocation {
+
+
+
+		public LocationImpl(String e) {
+			// TODO Auto-generated constructor stub
+		}
+
+		public SetLocation file(String file) {
+			this.file = file;
+			return this;
+		}
+
+		public SetLocation header(String header) {
+			this.header = header;
+			return this;
+		}
+
+		public SetLocation context(String context) {
+			this.context = context;
+			return this;
+		}
+
+		public SetLocation method(String methodName) {
+			this.methodName = methodName;
+			return this;
+		}
+
+		public SetLocation line(int line) {
+			this.line = line;
+			return this;
+		}
+
+		public SetLocation reference(String reference) {
+			this.reference = reference;
+			return this;
+		}
+		
+	}
+	
 	final Formatter		out;
 	boolean				trace;
 	boolean				pedantic;
 	boolean				exceptions;
-
+	
 	/**
 	 * @return the exceptions
 	 */
@@ -62,13 +105,14 @@
 		out = new Formatter(app);
 	}
 
-	public void error(String s, Object... args) {
+	public SetLocation error(String s, Object... args) {
 		String e = String.format(s, args);
 		errors.add(e);
 		trace("ERROR: %s", e);
+		return location(e);
 	}
 
-	public void exception(Throwable t, String s, Object... args) {
+	public SetLocation exception(Throwable t, String s, Object... args) {
 		String e = String.format(s, args);
 		errors.add(e);
 		trace("ERROR: %s", e);
@@ -77,15 +121,23 @@
 				t.getCause().printStackTrace(System.err);
 			else
 				t.printStackTrace(System.err);
+		return location(e);
 	}
 
-	public void warning(String s, Object... args) {
+	public SetLocation warning(String s, Object... args) {
 		String e = String.format(s, args);
 		warnings.add(e);
 		trace("warning: %s", e);
+		return location(e);
 	}
 
-	public void progress(String s, Object... args) {
+	private SetLocation location(String e) {
+		LocationImpl loc = new LocationImpl(e);
+		locations.add( loc );
+		return loc;
+	}
+
+	public void progress(float progress, String s, Object... args) {
 		if (out != null) {
 			out.format(s, args);
 			if (!s.endsWith("\n"))
@@ -178,4 +230,32 @@
 		}
 	}
 
+	public boolean getInfo(Report other) {
+		return getInfo(other,null);
+	}
+	public boolean getInfo(Report other, String prefix) {
+		boolean ok = true;
+		if ( prefix == null)
+			prefix = "";
+		else
+			prefix = prefix + ": ";
+		for ( String error : other.getErrors()) {
+			errors.add( prefix + error);
+			ok = false;
+		}
+		
+		for ( String warning : other.getWarnings()) {
+			warnings.add( prefix + warning);
+		}
+		return ok;
+	}
+
+	public Location getLocation(String msg) {
+		for ( LocationImpl loc : locations ) {
+			if ((loc.message != null) && loc.message.equals(msg))
+				return loc;
+		}
+		return null;
+	}
+
 }
diff --git a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
index eaf363c..375dbc6 100644
--- a/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
+++ b/bundleplugin/src/main/java/aQute/libg/reporter/ReporterMessages.java
@@ -1,19 +1,3 @@
-/*
- * Copyright (c) OSGi Alliance (2012). 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.reporter;
 
 import java.lang.reflect.*;
@@ -21,21 +5,55 @@
 
 import aQute.libg.reporter.Messages.ERROR;
 import aQute.libg.reporter.Messages.WARNING;
+import aQute.service.reporter.*;
+import aQute.service.reporter.Reporter.SetLocation;
 
 public class ReporterMessages {
 
+	static class WARNINGImpl implements ERROR {
+		Reporter.SetLocation	loc;
+
+		public SetLocation file(String file) {
+			return loc.file(file);
+		}
+
+		public SetLocation header(String header) {
+			return loc.header(header);
+		}
+
+		public SetLocation context(String context) {
+			return loc.context(context);
+		}
+
+		public SetLocation method(String methodName) {
+			return loc.method(methodName);
+		}
+
+		public SetLocation line(int n) {
+			return loc.line(n);
+		}
+
+		public SetLocation reference(String reference) {
+			return loc.reference(reference);
+		}
+
+		public WARNINGImpl(Reporter.SetLocation loc) {
+			this.loc = loc;
+		}
+	}
+
+	static class ERRORImpl extends WARNINGImpl implements WARNING {
+		public ERRORImpl(SetLocation e) {
+			super(e);
+		}
+	}
+
 	public static <T> T base(final Reporter reporter, Class<T> messages) {
 		return (T) Proxy.newProxyInstance(messages.getClassLoader(), new Class[] {
 			messages
 		}, new InvocationHandler() {
 
 			public Object invoke(Object target, Method method, Object[] args) throws Throwable {
-				if (reporter.isExceptions() && args!=null) {
-					for (Object o : args) {
-						if (o instanceof Throwable)
-							((Throwable) o).printStackTrace();
-					}
-				}
 				String format;
 				Message d = method.getAnnotation(Message.class);
 				if (d == null) {
@@ -63,9 +81,10 @@
 
 				try {
 					if (method.getReturnType() == ERROR.class) {
-						reporter.error(format, args);
+						return new ERRORImpl(reporter.error(format, args));
+
 					} else if (method.getReturnType() == WARNING.class) {
-						reporter.warning(format, args);
+						return new WARNINGImpl(reporter.warning(format, args));
 					} else
 						reporter.trace(format, args);
 				}
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/Domain.java b/bundleplugin/src/main/java/aQute/libg/sed/Domain.java
new file mode 100644
index 0000000..c030c83
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/Domain.java
@@ -0,0 +1,9 @@
+package aQute.libg.sed;
+
+import java.util.*;
+
+public interface Domain {
+	Map<String,String> getMap();
+
+	Domain getParent();
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java b/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
new file mode 100644
index 0000000..d62a014
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/libg/sed/ReplacerAdapter.java
@@ -0,0 +1,940 @@
+package aQute.libg.sed;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.collections.*;
+import aQute.lib.io.*;
+import aQute.libg.glob.*;
+import aQute.libg.reporter.*;
+import aQute.service.reporter.*;
+
+/**
+ * Provide a macro Domain. This Domain can replace variables in strings based on
+ * a properties and a domain. The domain can implement functions that start with
+ * a "_" and take args[], the names of these functions are available as
+ * functions in the macro Domain (without the _). Macros can nest to any depth
+ * but may not contain loops. Add POSIX macros: ${#parameter} String length.
+ * ${parameter%word} Remove smallest suffix pattern. ${parameter%%word} Remove
+ * largest suffix pattern. ${parameter#word} Remove smallest prefix pattern.
+ * ${parameter##word} Remove largest prefix pattern.
+ */
+public class ReplacerAdapter extends ReporterAdapter implements Replacer {
+	static final Random	random		= new Random();
+	static Pattern		WILDCARD	= Pattern.compile("[*?|[\\\\]\\(\\)]");
+	Domain				domain;
+	List<Object>		targets		= new ArrayList<Object>();
+	boolean				flattening;
+	File				base		= new File(System.getProperty("user.dir"));
+	Reporter			reporter	= this;
+
+	public ReplacerAdapter(Domain domain) {
+		this.domain = domain;
+	}
+
+	public ReplacerAdapter(final Map<String,String> domain) {
+		this(new Domain() {
+
+			public Map<String,String> getMap() {
+				return domain;
+			}
+
+			public Domain getParent() {
+				return null;
+			}
+			
+		});
+	}
+
+	public ReplacerAdapter target(Object target) {
+		assert target != null;
+		targets.add(target);
+		return this;
+	}
+
+	public ReplacerAdapter target(File base) {
+		this.base = base;
+		return this;
+	}
+
+	public String process(String line, Domain source) {
+		return process(line, new Link(source, null, line));
+	}
+
+	String process(String line, Link link) {
+		StringBuilder sb = new StringBuilder();
+		process(line, 0, '\u0000', '\u0000', sb, link);
+		return sb.toString();
+	}
+
+	int process(CharSequence org, int index, char begin, char end, StringBuilder result, Link link) {
+		StringBuilder line = new StringBuilder(org);
+		int nesting = 1;
+
+		StringBuilder variable = new StringBuilder();
+		outer: while (index < line.length()) {
+			char c1 = line.charAt(index++);
+			if (c1 == end) {
+				if (--nesting == 0) {
+					result.append(replace(variable.toString(), link));
+					return index;
+				}
+			} else if (c1 == begin)
+				nesting++;
+			else if (c1 == '\\' && index < line.length() - 1 && line.charAt(index) == '$') {
+				// remove the escape backslash and interpret the dollar
+				// as a
+				// literal
+				index++;
+				variable.append('$');
+				continue outer;
+			} else if (c1 == '$' && index < line.length() - 2) {
+				char c2 = line.charAt(index);
+				char terminator = getTerminator(c2);
+				if (terminator != 0) {
+					index = process(line, index + 1, c2, terminator, variable, link);
+					continue outer;
+				}
+			} else if (c1 == '.' && index < line.length() && line.charAt(index) == '/') {
+				// Found the sequence ./
+				if (index == 1 || Character.isWhitespace(line.charAt(index - 2))) {
+					// make sure it is preceded by whitespace or starts at begin
+					index++;
+					variable.append(base.getAbsolutePath());
+					variable.append('/');
+					continue outer;
+				}
+			}
+			variable.append(c1);
+		}
+		result.append(variable);
+		return index;
+	}
+
+	public static char getTerminator(char c) {
+		switch (c) {
+			case '(' :
+				return ')';
+			case '[' :
+				return ']';
+			case '{' :
+				return '}';
+			case '<' :
+				return '>';
+			case '\u00ab' : // Guillemet double << >>
+				return '\u00bb';
+			case '\u2039' : // Guillemet single
+				return '\u203a';
+		}
+		return 0;
+	}
+
+	public String getProcessed(String key) {
+		return replace(key, null);
+	}
+
+	protected String replace(String key, Link link) {
+		if (link != null && link.contains(key))
+			return "${infinite:" + link.toString() + "}";
+
+		if (key != null) {
+			key = key.trim();
+			if (key.length() > 0) {
+				Domain source = domain;
+				String value = null;
+				if (key.indexOf(';') < 0) {
+					if (WILDCARD.matcher(key).find()) {
+						Glob ins = new Glob(key);
+						StringBuilder sb = new StringBuilder();
+						String del = "";
+						for (String k : getAllKeys()) {
+							if (ins.matcher(k).find()) {
+								String v = replace(k, new Link(source, link, key));
+								if (v != null) {
+									sb.append(del);
+									del = ",";
+									sb.append(v);
+								}
+							}
+						}
+						return sb.toString();
+					}
+
+					while (value == null && source != null) {
+						value = source.getMap().get(key);
+						if (value != null)
+							return process(value, new Link(source, link, key));
+
+						source = source.getParent();
+					}
+				}
+				value = doCommands(key, link);
+				if (value != null)
+					return process(value, new Link(source, link, key));
+
+				if (key != null && key.trim().length() > 0) {
+					value = System.getProperty(key);
+					if (value != null)
+						return value;
+				}
+				if (!flattening && !key.equals("@"))
+					reporter.warning("No translation found for macro: " + key);
+			} else {
+				reporter.warning("Found empty macro key");
+			}
+		} else {
+			reporter.warning("Found null macro key");
+		}
+		return "${" + key + "}";
+	}
+
+	private List<String> getAllKeys() {
+		List<String> l = new ArrayList<String>();
+		Domain source = domain;
+		do {
+			l.addAll(source.getMap().keySet());
+			source = source.getParent();
+		} while (source != null);
+
+		Collections.sort(l);
+		return l;
+	}
+
+	/**
+	 * Parse the key as a command. A command consist of parameters separated by
+	 * ':'.
+	 * 
+	 * @param key
+	 * @return
+	 */
+	static Pattern	commands	= Pattern.compile("(?<!\\\\);");
+
+	private String doCommands(String key, Link source) {
+		String[] args = commands.split(key);
+		if (args == null || args.length == 0)
+			return null;
+
+		for (int i = 0; i < args.length; i++)
+			if (args[i].indexOf('\\') >= 0)
+				args[i] = args[i].replaceAll("\\\\;", ";");
+
+		if (args[0].startsWith("^")) {
+			String varname = args[0].substring(1).trim();
+
+			Domain parent = source.start.getParent();
+			if (parent != null)
+				return parent.getMap().get(varname);
+			else
+				return null;
+		}
+
+		Domain rover = domain;
+		while (rover != null) {
+			String result = doCommand(rover, args[0], args);
+			if (result != null)
+				return result;
+
+			rover = rover.getParent();
+		}
+
+		for (Object target : targets) {
+			String result = doCommand(target, args[0], args);
+			if (result != null)
+				return result;
+		}
+
+		return doCommand(this, args[0], args);
+	}
+
+	private String doCommand(Object target, String method, String[] args) {
+		if (target == null)
+			; // System.err.println("Huh? Target should never be null " +
+		// domain);
+		else {
+			String cname = "_" + method.replaceAll("-", "_");
+			try {
+				Method m = target.getClass().getMethod(cname, new Class[] {
+					String[].class
+				});
+				return (String) m.invoke(target, new Object[] {
+					args
+				});
+			}
+			catch (NoSuchMethodException e) {
+				// Ignore
+			}
+			catch (InvocationTargetException e) {
+				if (e.getCause() instanceof IllegalArgumentException) {
+					reporter.error("%s, for cmd: %s, arguments; %s", e.getMessage(), method, Arrays.toString(args));
+				} else {
+					reporter.warning("Exception in replace: " + e.getCause());
+					e.getCause().printStackTrace();
+				}
+			}
+			catch (Exception e) {
+				reporter.warning("Exception in replace: " + e + " method=" + method);
+				e.printStackTrace();
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Return a unique list where the duplicates are removed.
+	 * 
+	 * @param args
+	 * @return
+	 */
+	static String	_uniqHelp	= "${uniq;<list> ...}";
+
+	public String _uniq(String args[]) {
+		verifyCommand(args, _uniqHelp, null, 1, Integer.MAX_VALUE);
+		Set<String> set = new LinkedHashSet<String>();
+		for (int i = 1; i < args.length; i++) {
+			set.addAll(ExtList.from(args[i].trim()));
+		}
+		ExtList<String> rsult = new ExtList<String>();
+		rsult.addAll(set);
+		return rsult.join(",");
+	}
+
+	public String _pathseparator(String args[]) {
+		return File.pathSeparator;
+	}
+
+	public String _separator(String args[]) {
+		return File.separator;
+	}
+
+	public String _filter(String args[]) {
+		return filter(args, false);
+	}
+
+	public String _filterout(String args[]) {
+		return filter(args, true);
+
+	}
+
+	static String	_filterHelp	= "${%s;<list>;<regex>}";
+
+	String filter(String[] args, boolean include) {
+		verifyCommand(args, String.format(_filterHelp, args[0]), null, 3, 3);
+
+		ExtList<String> list = ExtList.from(args[1]);
+		Pattern pattern = Pattern.compile(args[2]);
+
+		for (Iterator<String> i = list.iterator(); i.hasNext();) {
+			if (pattern.matcher(i.next()).matches() == include)
+				i.remove();
+		}
+		return list.join();
+	}
+
+	static String	_sortHelp	= "${sort;<list>...}";
+
+	public String _sort(String args[]) {
+		verifyCommand(args, _sortHelp, null, 2, Integer.MAX_VALUE);
+
+		ExtList<String> result = new ExtList<String>();
+		for (int i = 1; i < args.length; i++) {
+			result.addAll(ExtList.from(args[i]));
+		}
+		Collections.sort(result);
+		return result.join();
+	}
+
+	static String	_joinHelp	= "${join;<list>...}";
+
+	public String _join(String args[]) {
+
+		verifyCommand(args, _joinHelp, null, 1, Integer.MAX_VALUE);
+
+		ExtList<String> result = new ExtList<String>();
+		for (int i = 1; i < args.length; i++) {
+			result.addAll(ExtList.from(args[i]));
+		}
+		return result.join();
+	}
+
+	static String	_ifHelp	= "${if;<condition>;<iftrue> [;<iffalse>] }";
+
+	public String _if(String args[]) {
+		verifyCommand(args, _ifHelp, null, 3, 4);
+		String condition = args[1].trim();
+		if (!condition.equalsIgnoreCase("false"))
+			if (condition.length() != 0)
+				return args[2];
+
+		if (args.length > 3)
+			return args[3];
+		else
+			return "";
+	}
+
+	public String _now(String args[]) {
+		return new Date().toString();
+	}
+
+	public final static String	_fmodifiedHelp	= "${fmodified;<list of filenames>...}, return latest modification date";
+
+	public String _fmodified(String args[]) throws Exception {
+		verifyCommand(args, _fmodifiedHelp, null, 2, Integer.MAX_VALUE);
+
+		long time = 0;
+		Collection<String> names = new ExtList<String>();
+		for (int i = 1; i < args.length; i++) {
+			names.addAll(ExtList.from(args[i]));
+		}
+		for (String name : names) {
+			File f = new File(name);
+			if (f.exists() && f.lastModified() > time)
+				time = f.lastModified();
+		}
+		return "" + time;
+	}
+
+	public String _long2date(String args[]) {
+		try {
+			return new Date(Long.parseLong(args[1])).toString();
+		}
+		catch (Exception e) {
+			e.printStackTrace();
+		}
+		return "not a valid long";
+	}
+
+	public String _literal(String args[]) {
+		if (args.length != 2)
+			throw new RuntimeException("Need a value for the ${literal;<value>} macro");
+		return "${" + args[1] + "}";
+	}
+
+	public String _def(String args[]) {
+		if (args.length != 2)
+			throw new RuntimeException("Need a value for the ${def;<value>} macro");
+
+		String value = domain.getMap().get(args[1]);
+		if (value == null)
+			return "";
+
+		return value;
+	}
+
+	/**
+	 * replace ; <list> ; regex ; replace
+	 * 
+	 * @param args
+	 * @return
+	 */
+	public String _replace(String args[]) {
+		if (args.length != 4) {
+			reporter.warning("Invalid nr of arguments to replace " + Arrays.asList(args));
+			return null;
+		}
+
+		String list[] = args[1].split("\\s*,\\s*");
+		StringBuilder sb = new StringBuilder();
+		String del = "";
+		for (int i = 0; i < list.length; i++) {
+			String element = list[i].trim();
+			if (!element.equals("")) {
+				sb.append(del);
+				sb.append(element.replaceAll(args[2], args[3]));
+				del = ", ";
+			}
+		}
+
+		return sb.toString();
+	}
+
+	public String _warning(String args[]) {
+		for (int i = 1; i < args.length; i++) {
+			reporter.warning(process(args[i]));
+		}
+		return "";
+	}
+
+	public String _error(String args[]) {
+		for (int i = 1; i < args.length; i++) {
+			reporter.error(process(args[i]));
+		}
+		return "";
+	}
+
+	/**
+	 * toclassname ; <path>.class ( , <path>.class ) *
+	 * 
+	 * @param args
+	 * @return
+	 */
+	static String	_toclassnameHelp	= "${classname;<list of class names>}, convert class paths to FQN class names ";
+
+	public String _toclassname(String args[]) {
+		verifyCommand(args, _toclassnameHelp, null, 2, 2);
+		Collection<String> paths = ExtList.from(args[1]);
+
+		ExtList<String> names = new ExtList<String>(paths.size());
+		for (String path : paths) {
+			if (path.endsWith(".class")) {
+				String name = path.substring(0, path.length() - 6).replace('/', '.');
+				names.add(name);
+			} else if (path.endsWith(".java")) {
+				String name = path.substring(0, path.length() - 5).replace('/', '.');
+				names.add(name);
+			} else {
+				reporter.warning("in toclassname, %s, is not a class path because it does not end in .class", args[1]);
+			}
+		}
+		return names.join(",");
+	}
+
+	/**
+	 * toclassname ; <path>.class ( , <path>.class ) *
+	 * 
+	 * @param args
+	 * @return
+	 */
+
+	static String	_toclasspathHelp	= "${toclasspath;<list>[;boolean]}, convert a list of class names to paths";
+
+	public String _toclasspath(String args[]) {
+		verifyCommand(args, _toclasspathHelp, null, 2, 3);
+		boolean cl = true;
+		if (args.length > 2)
+			cl = Boolean.valueOf(args[2]);
+
+		ExtList<String> names = ExtList.from(args[1]);
+		ExtList<String> paths = new ExtList<String>(names.size());
+		for (String name : names) {
+			String path = name.replace('.', '/') + (cl ? ".class" : "");
+			paths.add(path);
+		}
+		return paths.join(",");
+	}
+
+	public String _dir(String args[]) {
+		if (args.length < 2) {
+			reporter.warning("Need at least one file name for ${dir;...}");
+			return null;
+		} else {
+			String del = "";
+			StringBuilder sb = new StringBuilder();
+			for (int i = 1; i < args.length; i++) {
+				File f = IO.getFile(base, args[i]);
+				if (f.exists() && f.getParentFile().exists()) {
+					sb.append(del);
+					sb.append(f.getParentFile().getAbsolutePath());
+					del = ",";
+				}
+			}
+			return sb.toString();
+		}
+
+	}
+
+	public String _basename(String args[]) {
+		if (args.length < 2) {
+			reporter.warning("Need at least one file name for ${basename;...}");
+			return null;
+		} else {
+			String del = "";
+			StringBuilder sb = new StringBuilder();
+			for (int i = 1; i < args.length; i++) {
+				File f = IO.getFile(base, args[i]);
+				if (f.exists() && f.getParentFile().exists()) {
+					sb.append(del);
+					sb.append(f.getName());
+					del = ",";
+				}
+			}
+			return sb.toString();
+		}
+
+	}
+
+	public String _isfile(String args[]) {
+		if (args.length < 2) {
+			reporter.warning("Need at least one file name for ${isfile;...}");
+			return null;
+		} else {
+			boolean isfile = true;
+			for (int i = 1; i < args.length; i++) {
+				File f = new File(args[i]).getAbsoluteFile();
+				isfile &= f.isFile();
+			}
+			return isfile ? "true" : "false";
+		}
+
+	}
+
+	public String _isdir(String args[]) {
+		if (args.length < 2) {
+			reporter.warning("Need at least one file name for ${isdir;...}");
+			return null;
+		} else {
+			boolean isdir = true;
+			for (int i = 1; i < args.length; i++) {
+				File f = new File(args[i]).getAbsoluteFile();
+				isdir &= f.isDirectory();
+			}
+			return isdir ? "true" : "false";
+		}
+
+	}
+
+	public String _tstamp(String args[]) {
+		String format = "yyyyMMddHHmm";
+		long now = System.currentTimeMillis();
+		TimeZone tz = TimeZone.getTimeZone("UTC");
+
+		if (args.length > 1) {
+			format = args[1];
+		}
+		if (args.length > 2) {
+			tz = TimeZone.getTimeZone(args[2]);
+		}
+		if (args.length > 3) {
+			now = Long.parseLong(args[3]);
+		}
+		if (args.length > 4) {
+			reporter.warning("Too many arguments for tstamp: " + Arrays.toString(args));
+		}
+
+		SimpleDateFormat sdf = new SimpleDateFormat(format);
+		sdf.setTimeZone(tz);
+
+		return sdf.format(new Date(now));
+	}
+
+	/**
+	 * Wildcard a directory. The lists can contain Instruction that are matched
+	 * against the given directory ${lsr;<dir>;<list>(;<list>)*}
+	 * ${lsa;<dir>;<list>(;<list>)*}
+	 * 
+	 * @author aqute
+	 */
+
+	public String _lsr(String args[]) {
+		return ls(args, true);
+	}
+
+	public String _lsa(String args[]) {
+		return ls(args, false);
+	}
+
+	String ls(String args[], boolean relative) {
+		if (args.length < 2)
+			throw new IllegalArgumentException("the ${ls} macro must at least have a directory as parameter");
+
+		File dir = IO.getFile(base, args[1]);
+		if (!dir.isAbsolute())
+			throw new IllegalArgumentException("the ${ls} macro directory parameter is not absolute: " + dir);
+
+		if (!dir.exists())
+			throw new IllegalArgumentException("the ${ls} macro directory parameter does not exist: " + dir);
+
+		if (!dir.isDirectory())
+			throw new IllegalArgumentException(
+					"the ${ls} macro directory parameter points to a file instead of a directory: " + dir);
+
+		List<File> files = new ArrayList<File>(new SortedList<File>(dir.listFiles()));
+
+		for (int i = 2; i < args.length; i++) {
+			Glob filters = new Glob(args[i]);
+			filters.select(files);
+		}
+
+		ExtList<String> result = new ExtList<String>();
+		for (File file : files)
+			result.add(relative ? file.getName() : file.getAbsolutePath());
+
+		return result.join(",");
+	}
+
+	public String _currenttime(String args[]) {
+		return Long.toString(System.currentTimeMillis());
+	}
+
+	/**
+	 * System command. Execute a command and insert the result.
+	 * 
+	 * @param args
+	 * @param help
+	 * @param patterns
+	 * @param low
+	 * @param high
+	 */
+	public String system_internal(boolean allowFail, String args[]) throws Exception {
+		verifyCommand(args, "${" + (allowFail ? "system-allow-fail" : "system")
+				+ ";<command>[;<in>]}, execute a system command", null, 2, 3);
+		String command = args[1];
+		String input = null;
+
+		if (args.length > 2) {
+			input = args[2];
+		}
+
+		Process process = Runtime.getRuntime().exec(command, null, base);
+		if (input != null) {
+			process.getOutputStream().write(input.getBytes("UTF-8"));
+		}
+		process.getOutputStream().close();
+
+		String s = IO.collect(process.getInputStream(), "UTF-8");
+		int exitValue = process.waitFor();
+		if (exitValue != 0)
+			return exitValue + "";
+
+		if (!allowFail && (exitValue != 0)) {
+			reporter.error("System command " + command + " failed with " + exitValue);
+		}
+		return s.trim();
+	}
+
+	public String _system(String args[]) throws Exception {
+		return system_internal(false, args);
+	}
+
+	public String _system_allow_fail(String args[]) throws Exception {
+		String result = "";
+		try {
+			result = system_internal(true, args);
+		}
+		catch (Throwable t) {
+			/* ignore */
+		}
+		return result;
+	}
+
+	public String _env(String args[]) {
+		verifyCommand(args, "${env;<name>}, get the environmet variable", null, 2, 2);
+
+		try {
+			return System.getenv(args[1]);
+		}
+		catch (Throwable t) {
+			return null;
+		}
+	}
+
+	/**
+	 * Get the contents of a file.
+	 * 
+	 * @param in
+	 * @return
+	 * @throws IOException
+	 */
+
+	public String _cat(String args[]) throws IOException {
+		verifyCommand(args, "${cat;<in>}, get the content of a file", null, 2, 2);
+		File f = IO.getFile(base, args[1]);
+		if (f.isFile()) {
+			return IO.collect(f);
+		} else if (f.isDirectory()) {
+			return Arrays.toString(f.list());
+		} else {
+			try {
+				URL url = new URL(args[1]);
+				return IO.collect(url, "UTF-8");
+			}
+			catch (MalformedURLException mfue) {
+				// Ignore here
+			}
+			return null;
+		}
+	}
+
+	public static void verifyCommand(String args[], String help, Pattern[] patterns, int low, int high) {
+		String message = "";
+		if (args.length > high) {
+			message = "too many arguments";
+		} else if (args.length < low) {
+			message = "too few arguments";
+		} else {
+			for (int i = 0; patterns != null && i < patterns.length && i < args.length; i++) {
+				if (patterns[i] != null) {
+					Matcher m = patterns[i].matcher(args[i]);
+					if (!m.matches())
+						message += String.format("Argument %s (%s) does not match %s\n", i, args[i],
+								patterns[i].pattern());
+				}
+			}
+		}
+		if (message.length() != 0) {
+			StringBuilder sb = new StringBuilder();
+			String del = "${";
+			for (String arg : args) {
+				sb.append(del);
+				sb.append(arg);
+				del = ";";
+			}
+			sb.append("}, is not understood. ");
+			sb.append(message);
+			throw new IllegalArgumentException(sb.toString());
+		}
+	}
+
+	// Helper class to track expansion of variables
+	// on the stack.
+	static class Link {
+		Link	previous;
+		String	key;
+		Domain	start;
+
+		public Link(Domain start, Link previous, String key) {
+			this.previous = previous;
+			this.key = key;
+			this.start = start;
+		}
+
+		public boolean contains(String key) {
+			if (this.key.equals(key))
+				return true;
+
+			if (previous == null)
+				return false;
+
+			return previous.contains(key);
+		}
+
+		public String toString() {
+			StringBuilder sb = new StringBuilder("[");
+			append(sb);
+			sb.append("]");
+			return sb.toString();
+		}
+
+		private void append(StringBuilder sb) {
+			if (previous != null) {
+				previous.append(sb);
+				sb.append(",");
+			}
+			sb.append(key);
+		}
+	}
+
+	/**
+	 * Take all the properties and translate them to actual values. This method
+	 * takes the set properties and traverse them over all entries, including
+	 * the default properties for that properties. The values no longer contain
+	 * macros.
+	 * 
+	 * @return A new Properties with the flattened values
+	 */
+	public Map<String,String> getFlattenedProperties() {
+		// Some macros only work in a lower Domain, so we
+		// do not report unknown macros while flattening
+		flattening = true;
+		try {
+			Map<String,String> flattened = new HashMap<String,String>();
+			Map<String,String> source = domain.getMap();
+			for (String key : source.keySet()) {
+				if (!key.startsWith("_"))
+					if (key.startsWith("-"))
+						flattened.put(key, source.get(key));
+					else
+						flattened.put(key, process(source.get(key)));
+			}
+			return flattened;
+		}
+		finally {
+			flattening = false;
+		}
+	}
+
+	public final static String	_fileHelp	= "${file;<base>;<paths>...}, create correct OS dependent path";
+
+	public String _osfile(String args[]) {
+		verifyCommand(args, _fileHelp, null, 3, 3);
+		File base = new File(args[1]);
+		File f = IO.getFile(base, args[2]);
+		return f.getAbsolutePath();
+	}
+
+	public String _path(String args[]) {
+		ExtList<String> list = new ExtList<String>();
+		for (int i = 1; i < args.length; i++) {
+			list.addAll(ExtList.from(args[i]));
+		}
+		return list.join(File.pathSeparator);
+	}
+
+	public static Properties getParent(Properties p) {
+		try {
+			Field f = Properties.class.getDeclaredField("defaults");
+			f.setAccessible(true);
+			return (Properties) f.get(p);
+		}
+		catch (Exception e) {
+			Field[] fields = Properties.class.getFields();
+			System.err.println(Arrays.toString(fields));
+			return null;
+		}
+	}
+
+	public String process(String line) {
+		return process(line, domain);
+	}
+
+	/**
+	 * Generate a random string, which is guaranteed to be a valid Java
+	 * identifier (first character is an ASCII letter, subsequent characters are
+	 * ASCII letters or numbers). Takes an optional parameter for the length of
+	 * string to generate; default is 8 characters.
+	 */
+	public String _random(String[] args) {
+		int numchars = 8;
+		if (args.length > 1) {
+			try {
+				numchars = Integer.parseInt(args[1]);
+			}
+			catch (NumberFormatException e) {
+				throw new IllegalArgumentException("Invalid character count parameter in ${random} macro.");
+			}
+		}
+
+		char[] letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+		char[] alphanums = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
+
+		char[] array = new char[numchars];
+		for (int i = 0; i < numchars; i++) {
+			char c;
+			if (i == 0)
+				c = letters[random.nextInt(letters.length)];
+			else
+				c = alphanums[random.nextInt(alphanums.length)];
+			array[i] = c;
+		}
+
+		return new String(array);
+	}
+
+	public void setReporter(Reporter reporter) {
+		this.reporter = reporter;
+	}
+
+	
+	public int _processors(String args[]) {
+		float multiplier = 1F;
+		if ( args.length > 1 )
+			multiplier = Float.parseFloat(args[1]);
+		
+		return (int) (Runtime.getRuntime().availableProcessors() * multiplier);
+	}
+	
+	public long _maxMemory(String args[]) {
+		return Runtime.getRuntime().maxMemory();
+	}
+	public long _freeMemory(String args[]) {
+		return Runtime.getRuntime().freeMemory();
+	}
+
+	public long _nanoTime(String args[]) {
+		return System.nanoTime();
+	}
+	
+}
diff --git a/bundleplugin/src/main/java/aQute/libg/sed/Sed.java b/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
index 4642c92..0542f65 100644
--- a/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
+++ b/bundleplugin/src/main/java/aQute/libg/sed/Sed.java
@@ -4,7 +4,7 @@
 import java.util.*;
 import java.util.regex.*;
 
-import aQute.lib.io.IO;
+import aQute.lib.io.*;
 
 public class Sed {
 	final File					file;
diff --git a/bundleplugin/src/main/java/aQute/service/reporter/Report.java b/bundleplugin/src/main/java/aQute/service/reporter/Report.java
new file mode 100644
index 0000000..a637c8e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/service/reporter/Report.java
@@ -0,0 +1,53 @@
+package aQute.service.reporter;
+
+import java.util.*;
+
+/**
+ * A base interface to represent the state of a work in progress.
+ */
+public interface Report {
+	/**
+	 * Defines a record for the location of an error/warning
+	 */
+	class Location {
+		public String	message;
+		public int		line;
+		public String	file;
+		public String	header;
+		public String	context;
+		public String	reference;
+		public String	methodName;
+	}
+
+
+	/**
+	 * Return the warnings. This list must not be changed and may be immutable.
+	 * 
+	 * @return the warnings
+	 */
+	List<String> getWarnings();
+
+	/**
+	 * Return the errors. This list must not be changed and may be immutable.
+	 * 
+	 * @return the errors
+	 */
+	List<String> getErrors();
+
+	/**
+	 * Return the errors for the given error or warning. Can return null.
+	 * 
+	 * @param msg
+	 *            The message
+	 * @return null or the location of the message
+	 */
+	Location getLocation(String msg);
+
+	/**
+	 * Check if this report has any errors.
+	 * 
+	 * @return true if errors is empty
+	 */
+	
+	boolean isOk();
+}
diff --git a/bundleplugin/src/main/java/aQute/service/reporter/Reporter.java b/bundleplugin/src/main/java/aQute/service/reporter/Reporter.java
new file mode 100755
index 0000000..521ec5e
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/service/reporter/Reporter.java
@@ -0,0 +1,130 @@
+package aQute.service.reporter;
+
+/**
+ * A base interface to model a work in progress. Though exceptions work well for
+ * major, well, exceptions, they are lousy for reporting errors/warnings in a
+ * task. Logging also sucks because it is global, hard to relate to a single
+ * piece of work. This small (uncoupled) interface is intended to fill this gap.
+ * The idea is that different tasks can perform parts and the progress can be
+ * integrated. A reporter is not mandated to track locations. Locations should
+ * be seen as best effort.
+ */
+public interface Reporter extends Report {
+	/**
+	 * Fluid interface to set location data
+	 */
+	interface SetLocation {
+		/**
+		 * Set the file location
+		 */
+		SetLocation file(String file);
+
+		/**
+		 * Set the header/section location. This is normally the header in a
+		 * manifest or properties file.
+		 */
+		SetLocation header(String header);
+
+		/**
+		 * Set the context in the header.
+		 * 
+		 * @param context
+		 * @return
+		 */
+		SetLocation context(String context);
+
+		/**
+		 * Set the method where the error is reported.
+		 * 
+		 * @param context
+		 * @return
+		 */
+		SetLocation method(String methodName);
+
+		/**
+		 * Set the line number. Line 0 is the top line.
+		 */
+		SetLocation line(int n);
+
+		/**
+		 * Set a reference for the error (url or so)
+		 */
+
+		SetLocation reference(String reference);
+	}
+
+	/**
+	 * Create an error. Implementations must ensure that the given args are not
+	 * prevented from garbage collecting. The args must have a proper toString
+	 * method.
+	 * 
+	 * @param format
+	 *            The format of the error
+	 * @param args
+	 *            The arguments of the error
+	 * @return a SetLocation to set the location
+	 */
+	SetLocation error(String format, Object... args);
+
+	/**
+	 * Create a warning. Implementations must ensure that the given args are not
+	 * prevented from garbage collecting. The args must have a proper toString
+	 * method.
+	 * 
+	 * @param format
+	 *            The format of the error
+	 * @param args
+	 *            The arguments of the error
+	 * @return a SetLocation to set the location
+	 */
+	SetLocation warning(String format, Object... args);
+
+	/**
+	 * Create a warning. Implementations must ensure that the given args are not
+	 * prevented from garbage collecting. The args must have a proper toString
+	 * method.
+	 * 
+	 * @param format
+	 *            The format of the error
+	 * @param args
+	 *            The arguments of the error
+	 */
+	void trace(String format, Object... args);
+
+	/**
+	 * Create a warning. Implementations must ensure that the given args are not
+	 * prevented from garbage collecting. The args must have a proper toString
+	 * method.
+	 * 
+	 * @param progress
+	 *            A value between 0 and 1 indicating the progress. 0 is starting
+	 *            and >=1 is done.
+	 * @param format
+	 *            The format of the error
+	 * @param args
+	 *            The arguments of the error
+	 * @return a SetLocation to set the location
+	 */
+	void progress(float progress, String format, Object... args);
+
+	/**
+	 * Dedicated message for an exception.
+	 * 
+	 * @param t
+	 *            The exception
+	 * @param s
+	 *            The format of the message
+	 * @param args
+	 *            The arguments
+	 */
+	public SetLocation exception(Throwable t, String format, Object... args);
+
+	/**
+	 * The provider of the reporter wants pedantic reporting, meaning every
+	 * possible warning should be reported.
+	 * 
+	 * @return if this is a pedantic reporter.
+	 */
+	boolean isPedantic();
+
+}
diff --git a/bundleplugin/src/main/java/aQute/service/reporter/packageinfo b/bundleplugin/src/main/java/aQute/service/reporter/packageinfo
new file mode 100644
index 0000000..9ad81f6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/service/reporter/packageinfo
@@ -0,0 +1 @@
+version 1.0.0
diff --git a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
index 0df974d..746bb26 100644
--- a/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
+++ b/bundleplugin/src/main/java/org/apache/felix/bundleplugin/BlueprintPlugin.java
@@ -47,7 +47,7 @@
 import aQute.lib.osgi.Resource;
 import aQute.libg.generics.Create;
 import aQute.libg.qtokens.QuotedTokenizer;
-import aQute.libg.reporter.Reporter;
+import aQute.service.reporter.Reporter;
 
 
 public class BlueprintPlugin implements AnalyzerPlugin