[jboss-osgi-commits] JBoss-OSGI SVN: r94060 - in projects/jboss-osgi/projects: aQute and 56 other directories.

jboss-osgi-commits at lists.jboss.org jboss-osgi-commits at lists.jboss.org
Mon Sep 28 09:04:10 EDT 2009


Author: thomas.diesler at jboss.com
Date: 2009-09-28 09:04:06 -0400 (Mon, 28 Sep 2009)
New Revision: 94060

Added:
   projects/jboss-osgi/projects/aQute/
   projects/jboss-osgi/projects/aQute/trunk/
   projects/jboss-osgi/projects/aQute/trunk/.project
   projects/jboss-osgi/projects/aQute/trunk/.settings/
   projects/jboss-osgi/projects/aQute/trunk/.settings/org.maven.ide.eclipse.prefs
   projects/jboss-osgi/projects/aQute/trunk/bnd/
   projects/jboss-osgi/projects/aQute/trunk/bnd/.project
   projects/jboss-osgi/projects/aQute/trunk/bnd/.settings/
   projects/jboss-osgi/projects/aQute/trunk/bnd/.settings/org.maven.ide.eclipse.prefs
   projects/jboss-osgi/projects/aQute/trunk/bnd/pom.xml
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activate.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activator.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attribute.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attributes.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Component.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/ConfigurationPolicy.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Deactivate.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Exclude.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Export.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Include.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Mandatory.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Modified.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Optional.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Reference.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Test.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Unreference.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/package-info.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BaseTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BndTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/DeployTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/EclipseTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ExpandPropertiesTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/PrepareTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ProjectTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/WrapTask.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/CircularDependencyException.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Container.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Project.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ProjectBuilder.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ReflectAction.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ScriptAction.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Workspace.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerInitializer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerPage.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/ModelListener.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/help/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/help/Syntax.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarConfiguration.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarDocumentProvider.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarEditor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiArgumentsTab.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLaunchShortcut.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitTabGroup.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchDelegate.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchTabGroup.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/Shortcut.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/bnd.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/packageinfo
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/AnnotationReader.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ComponentDef.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/Make.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeBnd.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeCopy.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ServiceComponent.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/BsnToMavenPath.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenGroup.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenRepository.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/ActionWrapper.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Activator.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Central.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndBuilder.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndNature.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/ToggleNatureAction.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndCompletionProcessor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndHover.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditorContributor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndScanner.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndSourceViewerConfiguration.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndTextEditor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/MacroRule.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/Scripts.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/SubMenu.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/AddToRepo.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/InstallBundle.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/MakeBundle.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/VerifyBundle.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/WrapBundle.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/repo/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/repo/RepoDialog.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/AnalyzerPlugin.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/DependencyContributor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/MakePlugin.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Plugin.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Refreshable.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/RepositoryPlugin.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/SignerPlugin.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/Action.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/NamedAction.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/set/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/set/Group.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/JartoolSigner.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/Signer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/test/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/test/ProjectLauncher.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/base64/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/base64/Base64.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/deployer/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/deployer/FileRepo.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/jardiff/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/jardiff/Diff.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/About.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/AbstractResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Analyzer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Annotation.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Builder.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ClassDataCollector.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Clazz.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Constants.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/EmbeddedResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/FileResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Instruction.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/InstructionFilter.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Jar.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/JarResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Macro.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/OpCodes.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/PreprocessResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Processor.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Resource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/URLResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Verifier.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/WriteResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ZipResource.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/eclipse/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/tag/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/tag/Tag.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/generics/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/generics/Create.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/header/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/header/OSGiHeader.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/qtokens/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/reporter/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/reporter/Reporter.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Replacer.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Sed.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/Version.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/VersionRange.java
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/active.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/bundle.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/export-package.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/fragment.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/host.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/import-package.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/importing-bundle.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/installed.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/inuse.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/logo16x16.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/mini-logo.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/module.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/property.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/registered.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/resolved.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/run.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/test.gif
   projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/unknown.gif
   projects/jboss-osgi/projects/aQute/trunk/pom.xml
   projects/jboss-osgi/projects/aQute/trunk/runtime/
   projects/jboss-osgi/projects/aQute/trunk/runtime/.classpath
   projects/jboss-osgi/projects/aQute/trunk/runtime/.project
   projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/
   projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.eclipse.jdt.core.prefs
   projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.maven.ide.eclipse.prefs
   projects/jboss-osgi/projects/aQute/trunk/runtime/pom.xml
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/BasicTestReport.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/GenericFramework.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/JUnitReport.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/SimplePermissionPolicy.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/Target.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/TestReporter.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/XMLReport.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/Context.java
   projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/MiniFramework.java
Log:
Initial import of 0.0.356 sources

Added: projects/jboss-osgi/projects/aQute/trunk/.project
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/.project	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/.project	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>parent</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+	</natures>
+</projectDescription>

Added: projects/jboss-osgi/projects/aQute/trunk/.settings/org.maven.ide.eclipse.prefs
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/.settings/org.maven.ide.eclipse.prefs	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/.settings/org.maven.ide.eclipse.prefs	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+#Mon Sep 28 13:09:43 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/.project
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/.project	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/.project	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bnd</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/.settings/org.maven.ide.eclipse.prefs
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/.settings/org.maven.ide.eclipse.prefs	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/.settings/org.maven.ide.eclipse.prefs	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+#Mon Sep 28 14:10:56 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/pom.xml
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/pom.xml	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/pom.xml	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+  <!-- ====================================================================== -->
+  <!--                                                                        -->
+  <!--  JBoss, the OpenSource J2EE webOS                                      -->
+  <!--                                                                        -->
+  <!--  Distributable under LGPL license.                                     -->
+  <!--  See terms of license at http://www.gnu.org.                           -->
+  <!--                                                                        -->
+  <!-- ====================================================================== -->
+
+  <!-- $Id: pom.xml 93592 2009-09-16 09:00:08Z thomas.diesler at jboss.com $
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <name>aQute - Bnd</name>
+
+  <groupId>biz.aQute</groupId>
+  <artifactId>bnd</artifactId>
+  <packaging>jar</packaging>
+
+  <!-- Parent -->
+  <parent>
+    <groupId>biz.aQute</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.0.356-SNAPSHOT</version>
+  </parent>
+
+  <!-- Dependencies -->
+  <dependencies>
+    <dependency>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <version>3.2.0-v20060601</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.equinox</groupId>
+      <artifactId>org.eclipse.equinox.common</artifactId>
+      <version>3.2.0-v20060603</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.core</groupId>
+      <artifactId>org.eclipse.core.resources</artifactId>
+      <version>3.2.0-v20060603</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.core</groupId>
+      <artifactId>org.eclipse.core.runtime</artifactId>
+      <version>3.2.0-v20060603</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.core</groupId>
+      <artifactId>org.eclipse.core.jobs</artifactId>
+      <version>3.2.0-v20060603</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.equinox</groupId>
+      <artifactId>org.eclipse.equinox.registry</artifactId>
+      <version>3.2.0-v20060601</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jface</groupId>
+      <artifactId>org.eclipse.jface</artifactId>
+      <version>3.2.0-I20060605-1400</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.ui</groupId>
+      <artifactId>org.eclipse.ui.workbench</artifactId>
+      <version>3.2.0-I20060605-1400</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.swt</groupId>
+      <artifactId>org.eclipse.swt.win32.win32.x86</artifactId>
+      <version>3.2.0-v3232</version>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.jdt</groupId>
+      <artifactId>org.eclipse.jdt.core</artifactId>
+      <version>3.2.0-v_671</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.ant</groupId>
+      <artifactId>ant</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activate.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activate.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activate.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.METHOD)
+public @interface Activate {}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activator.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activator.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Activator.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,11 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+enum Policy { LAZY, EAGER }
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.TYPE)
+public @interface Activator {
+    Policy policy() default Policy.LAZY;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attribute.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attribute.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attribute.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,6 @@
+package aQute.bnd.annotation;
+
+public @interface Attribute {
+    String key();
+    String value();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attributes.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attributes.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Attributes.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.PACKAGE)
+public @interface Attributes {
+    Attribute[] value();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Component.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Component.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Component.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,16 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.TYPE)
+public @interface Component {
+    String name() default "";
+    Class<?>[] provides() default Object.class;
+    String factory() default "*";
+    boolean serviceFactory() default false;
+    boolean enabled() default true;
+    boolean immediate() default false;
+    ConfigurationPolicy configurationPolicy() default ConfigurationPolicy.OPTIONAL;
+    
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/ConfigurationPolicy.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/ConfigurationPolicy.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/ConfigurationPolicy.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,5 @@
+package aQute.bnd.annotation;
+
+public enum ConfigurationPolicy {
+    OPTIONAL, REQUIRE, IGNORE
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Deactivate.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Deactivate.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Deactivate.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.METHOD)
+public @interface Deactivate {}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Exclude.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Exclude.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Exclude.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.TYPE)
+public @interface Exclude {}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Export.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Export.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Export.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.PACKAGE)
+public @interface Export {
+    String value() default "";
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Include.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Include.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Include.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.TYPE)
+public @interface Include {}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Mandatory.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Mandatory.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Mandatory.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.PACKAGE)
+public @interface Mandatory {
+    String[] value() default "";
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Modified.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Modified.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Modified.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.METHOD)
+public @interface Modified {}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Optional.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Optional.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Optional.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.PACKAGE)
+public @interface Optional {
+    Class<?>[] value();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Reference.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Reference.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Reference.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,13 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.METHOD)
+public @interface Reference {
+    boolean optional() default false;
+    boolean multiple() default false;
+    boolean dynamic() default false;
+    String target() default "";
+    char type() default 0;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Test.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Test.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Test.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,29 @@
+package aQute.bnd.annotation;
+
+import java.io.*;
+
+import org.osgi.service.log.*;
+
+ at Component(provides=Serializable.class,name="abc",configurationPolicy=ConfigurationPolicy.OPTIONAL)
+public class Test implements Serializable {
+
+    @Activate
+    private void activate() {
+        
+    }
+    
+    @Deactivate
+    private void deactivate() {
+        
+    }
+
+    @Modified
+    private void modified() {
+        
+    }
+    
+    @Reference(optional=true)
+    public void setLog(LogService log) {
+        
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Unreference.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Unreference.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/Unreference.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,8 @@
+package aQute.bnd.annotation;
+
+import java.lang.annotation.*;
+
+ at Retention(RetentionPolicy.CLASS)
+ at Target(ElementType.METHOD)
+public @interface Unreference {
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/package-info.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/package-info.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/annotation/package-info.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,6 @@
+
+ at Mandatory({"a","b"})
+ at Export("3.21")
+
+ at Attributes(@Attribute(key="a", value="b"))
+package aQute.bnd.annotation;

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BaseTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BaseTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BaseTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,119 @@
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+
+import aQute.libg.reporter.*;
+
+public class BaseTask extends Task implements Reporter {
+    List<String>    errors   = new ArrayList<String>();
+    List<String>    warnings = new ArrayList<String>();
+    List<String>    progress = new ArrayList<String>();
+    boolean pedantic;
+    boolean trace;
+
+    public void error(String s, Object... args ) {
+        errors.add(String.format(s, args));
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    public List<String> getProgress() {
+        return progress;
+    }
+
+    public List<String> getWarnings() {
+        // TODO Auto-generated method stub
+        return warnings;
+    }
+
+    public void progress(String s, Object ... args) {
+        progress.add(String.format(s,args));
+    }
+
+    public void warning(String s, Object ... args) {
+        warnings.add(String.format(s, args));
+    }
+
+    protected boolean report() {
+        return report(this);
+    }
+
+    protected boolean report(Reporter reporter) {
+        if (reporter.getWarnings().size() > 0) {
+            System.err.println("Warnings");
+            for (Iterator<String> e = reporter.getWarnings().iterator(); e.hasNext();) {
+                System.err.println(e.next());
+            }
+        }
+        if (reporter.getErrors().size() > 0) {
+            System.err.println("Errors");
+            for (Iterator<String> e = reporter.getErrors().iterator(); e.hasNext();) {
+                System.err.println(e.next());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public static File getFile(File base, String file)  {
+        File f = new File(file);
+        if (!f.isAbsolute()) {
+            int n;
+
+            f = base.getAbsoluteFile();
+            while ((n = file.indexOf('/')) > 0) {
+                String first = file.substring(0, n);
+                file = file.substring(n + 1);
+                if (first.equals(".."))
+                    f = f.getParentFile();
+                else
+                    f = new File(f, first);
+            }
+            f = new File(f, file);
+        }
+        try {
+            return f.getCanonicalFile();
+        } catch(IOException e ) {
+            return f.getAbsoluteFile();
+        }
+    }
+
+    protected List<String> split(String dependsOn, String string) {
+        if (dependsOn == null)
+            return new ArrayList<String>();
+
+        return Arrays.asList(string.split("\\s*" + string + "\\s*"));
+    }
+
+    protected String join(Collection<?> classpath, String string) {
+        StringBuffer sb = new StringBuffer();
+        String del = "";
+        for (Object name : classpath) {
+            sb.append(del);
+            sb.append(name);
+            del = string;
+        }
+        return sb.toString();
+    }
+
+    public boolean isPedantic() {
+        return pedantic;
+    }
+
+    public void setPedantic(boolean pedantic) {
+        this.pedantic = pedantic;
+    }
+    public void setTrace(boolean trace) {
+        this.trace = trace;
+    }
+
+    public void trace(String s, Object... args) {
+        System.out.printf("# "+s+"\n", args);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BndTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BndTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/BndTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,267 @@
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.build.Project;
+import aQute.lib.osgi.*;
+import aQute.lib.osgi.eclipse.*;
+import aQute.libg.qtokens.*;
+
+public class BndTask extends BaseTask {
+    String  command;
+    File    basedir;
+
+    boolean failok;
+    boolean exceptions;
+    boolean print;
+
+    public void execute() throws BuildException {
+        if (command == null) {
+            executeBackwardCompatible();
+            return;
+
+        }
+
+        if ( basedir == null )
+            throw new BuildException("No basedir set");
+        
+        try {
+            Project project = Workspace.getProject(basedir);
+            project.setProperty("in.ant", "true");
+            project.setProperty("environment", "ant");
+            project.setExceptions(true);
+            project.action(command);
+            
+            if ( report(project) )
+                throw new BuildException("Command " + command + " failed");
+        } catch (Throwable e) {
+            if ( exceptions)
+                e.printStackTrace();
+            throw new BuildException(e);
+        }
+
+    }
+
+    public void setCommand(String command) {
+        this.command = command;
+    }
+
+    /**
+     * Set the base directory of the project. This property MUST be set.
+     * 
+     * @param basedir
+     */
+    public void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+
+    // Old shit
+
+    List<File> files      = new ArrayList<File>();
+    List<File> classpath  = new ArrayList<File>();
+    List<File> sourcepath = new ArrayList<File>();
+    File       output     = null;
+    File       testDir    = null;
+    boolean    eclipse;
+    boolean    inherit    = true;
+
+    private void executeBackwardCompatible() throws BuildException {
+        try {
+            if (files == null)
+                throw new BuildException("No files set");
+
+            if (eclipse) {
+                File project = getProject().getBaseDir();
+                EclipseClasspath cp = new EclipseClasspath(this, project
+                        .getParentFile(), project);
+                classpath.addAll(cp.getClasspath());
+                classpath.addAll(cp.getBootclasspath());
+                sourcepath.addAll(cp.getSourcepath());
+                // classpath.add(cp.getOutput());
+                if (report())
+                    throw new BuildException(
+                            "Errors during Eclipse Path inspection");
+
+            }
+
+            if (output == null)
+                output = getProject().getBaseDir();
+
+            for (Iterator<File> f = files.iterator(); f.hasNext();) {
+                File file = (File) f.next();
+                Builder builder = new Builder();
+
+                // Get the ant properties as a base.
+                if (inherit) {
+                    Properties p = new Properties();
+                    p.putAll((Map<?, ?>) getProject().getProperties());
+                    builder.setProperties(p);
+                }
+
+                builder.setPedantic(isPedantic());
+                if (file.exists()) {
+                    // Do nice property calculations
+                    // merging includes etc.
+                    builder.setProperties(file);
+                }
+
+                // get them and merge them with the project
+                // properties
+                Properties projectProperties = new Properties();
+                projectProperties.putAll((Map<?, ?>) getProject()
+                        .getProperties());
+                projectProperties.putAll(builder.getProperties());
+                builder.setProperties(projectProperties);
+                builder.setClasspath(toFiles(classpath, "classpath"));
+                builder.setSourcepath(toFiles(sourcepath, "sourcepath"));
+                Jar jars[] = builder.builds();
+
+                if (!failok && report(builder)) {
+                    throw new BuildException("bnd failed", new Location(file
+                            .getAbsolutePath()));
+                }
+
+                for (int i = 0; i < jars.length; i++) {
+                    Jar jar = jars[i];
+                    String bsn = jar.getName();
+
+                    File base = file.getParentFile();
+                    File output = this.output;
+
+                    String path = builder.getProperty("-output");
+
+                    if (output == null) {
+                        if (path == null)
+                            output = getFile(base, bsn + ".jar");
+                        else {
+                            output = getFile(base, path);
+                        }
+                    } else if (output.isDirectory()) {
+                        if (path == null)
+                            output = getFile(this.output, bsn + ".jar");
+                        else
+                            output = getFile(this.output, path);
+                    } else if (output.isFile()) {
+                        if (files.size() > 1)
+                            error("Output is a file but there are multiple input files, these files will overwrite the output file: "
+                                    + output.getAbsolutePath());
+                    }
+
+                    String msg = "";
+                    if (!output.exists()
+                            || output.lastModified() <= jar.lastModified()) {
+                        jar.write(output);
+                    } else {
+                        msg = "(not modified)";
+                    }
+                    trace(jar.getName() + " (" + output.getName()
+                            + ") " + jar.getResources().size() + " " + msg);
+                    report();
+                    jar.close();
+                }
+                builder.close();
+            }
+        } catch (Exception e) {
+            // if (exceptions)
+            e.printStackTrace();
+            if (!failok)
+                throw new BuildException("Failed to build jar file: ", e);
+        }
+    }
+
+    public void setFiles(String files) {
+        files = files.replaceAll("\\.jar(,|$)", ".bnd");
+        addAll(this.files, files, ",");
+    }
+
+    void addAll(List<File> list, String files, String separator) {
+        QuotedTokenizer qt = new QuotedTokenizer(files, separator);
+        String entries[] = qt.getTokens();
+        File project = getProject().getBaseDir();
+        for (int i = 0; i < entries.length; i++) {
+            File f = getFile(project, entries[i]);
+            if (f.exists())
+                list.add(f);
+            else
+                error("Can not find bnd file to process: "
+                        + f.getAbsolutePath());
+        }
+    }
+
+    public void setClasspath(String value) {
+        Path p = (Path) getProject().getReference(value);
+        if (p == null)
+            addAll(classpath, value, File.pathSeparator + ",");
+        else {
+            String[] path = p.list();
+            for (int i = 0; i < path.length; i++)
+                classpath.add(new File(path[i]));
+        }
+    }
+
+    public void setClasspath(Path p) {
+        String[] path = p.list();
+        for (int i = 0; i < path.length; i++)
+            classpath.add(new File(path[i]));
+    }
+
+    public void setEclipse(boolean eclipse) {
+        this.eclipse = eclipse;
+    }
+
+    boolean isFailok() {
+        return failok;
+    }
+
+    public void setFailok(boolean failok) {
+        this.failok = failok;
+    }
+
+    boolean isExceptions() {
+        return exceptions;
+    }
+
+    public void setExceptions(boolean exceptions) {
+        this.exceptions = exceptions;
+    }
+
+    boolean isPrint() {
+        return print;
+    }
+
+    void setPrint(boolean print) {
+        this.print = print;
+    }
+
+    public void setSourcepath(String sourcepath) {
+        addAll(this.sourcepath, sourcepath, File.pathSeparator + ",");
+    }
+
+    static File[] EMPTY_FILES = new File[0];
+
+    File[] toFiles(List<File> files, String what) throws IOException {
+        return files.toArray(EMPTY_FILES);
+    }
+
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    public void setDestFile(File output) {
+        this.output = output;
+    }
+
+    public void setTestDir(File testDir) {
+        this.testDir = testDir;
+    }
+
+    public void setInherit(boolean inherit) {
+        this.inherit = inherit;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/DeployTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/DeployTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/DeployTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,52 @@
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.build.Project;
+
+public class DeployTask extends BaseTask {
+    List<FileSet> filesets = new ArrayList<FileSet>();
+
+    @SuppressWarnings("unchecked")
+    public void execute() throws BuildException {
+        try {
+            Project project = Workspace.getProject(getProject().getBaseDir());
+
+            // Deploy the files that need to be released
+            for (FileSet fileset : filesets) {
+                DirectoryScanner ds = fileset.getDirectoryScanner(getProject());
+                String[] files = ds.getIncludedFiles();
+                if (files.length == 0)
+                    trace("No files included");
+
+                for (int i = 0; i < files.length; i++) {
+                    File file = new File(ds.getBasedir(), files[i]);
+                    try {
+                        if (file.isFile() && file.getName().endsWith(".jar")) {
+                            project.deploy(file);
+                        } else
+                            error("Not a jar file: " + file);
+                    } catch (Exception e) {
+                        error("Failed to deploy " + file + " : " + e);
+                    }
+                }
+            }
+            report(project);
+            if (project.getErrors().size() > 0)
+                throw new BuildException("Deploy failed");
+        } catch (Throwable t) {
+            t.printStackTrace();
+            throw new BuildException(t);
+        }
+    }
+
+    public void addFileset(FileSet files) {
+        this.filesets.add(files);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/EclipseTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/EclipseTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/EclipseTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,99 @@
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+
+import aQute.lib.osgi.eclipse.*;
+
+public class EclipseTask extends BaseTask {
+    private String     prefix    = "project.";
+    private List<File> prebuild  = new ArrayList<File>();
+    private File       workspaceLocation;
+    private String     separator = ",";
+    private File       projectLocation;
+
+    public void execute() throws BuildException {
+        try {
+            if (projectLocation == null)
+                projectLocation = getProject().getBaseDir();
+
+            if (workspaceLocation == null)
+                workspaceLocation = projectLocation.getParentFile();
+
+            EclipseClasspath eclipse = new EclipseClasspath(this, workspaceLocation,
+                    projectLocation);
+
+            if (report())
+                throw new BuildException(
+                        "Errors during Eclipse Path inspection");
+
+            addProperty(prefix + "classpath", join(eclipse.getClasspath(),
+                    separator));
+
+            addProperty(prefix + "bootclasspath", join(eclipse
+                    .getBootclasspath(), separator));
+
+            if (!eclipse.getSourcepath().isEmpty())
+                addProperty(prefix + "sourcepath", join(
+                        eclipse.getSourcepath(), separator));
+
+            addProperty(prefix + "output", eclipse.getOutput()
+                    .getAbsolutePath());
+
+            /**
+             * The prebuild is an attribute that is prepended to the dependency
+             * path derived from the Eclipse project
+             */
+
+            List<File> dependents = new ArrayList<File>();
+            addCareful(dependents, prebuild);
+            addCareful(dependents, eclipse.getDependents());
+            if (dependents.size() > 0) {
+                addProperty(prefix + "buildpath", join(dependents, separator));
+            }
+        } catch (Exception e) {
+            throw new BuildException(
+                    "Error during parsing Eclipse .classpath files", e);
+        }
+    }
+
+    private void addCareful(List<File> result, Collection<File> projects) {
+        for (Iterator<File> i = projects.iterator(); i.hasNext();) {
+            File d = i.next();
+            if (!result.contains(d))
+                result.add(d);
+        }
+    }
+
+    protected void addProperty(String n, String v) {
+        // System.out.println(" Adding property: " + n + " = " + v);
+        if (v != null)
+            getProject().setProperty(n, v);
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public void setPrebuild(String prebuild) {
+        StringTokenizer st = new StringTokenizer(prebuild, " ,");
+        while (st.hasMoreTokens()) {
+            this.prebuild.add(getFile(
+                    getProject().getBaseDir().getParentFile(), st.nextToken()));
+        }
+    }
+
+    public void setSeparator(String separator) {
+        this.separator = separator;
+    }
+
+    public void setProjectLocation(File projectLocation) {
+        this.projectLocation = projectLocation;
+    }
+
+    public void setWorkspaceLocation(File workspaceLocation) {
+        this.workspaceLocation = workspaceLocation;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ExpandPropertiesTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ExpandPropertiesTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ExpandPropertiesTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,41 @@
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+
+import aQute.lib.osgi.*;
+
+public class ExpandPropertiesTask extends BaseTask {
+	File	propertyFile;
+
+	public void execute() throws BuildException {
+		try {
+			if (propertyFile.exists()) {
+				Properties properties = new Properties();
+				properties.putAll((Map<?,?>)getProject().getProperties());
+				
+				Processor   processor = new Processor(properties);
+				processor.setProperties(propertyFile);
+
+				Project project = getProject();
+				Properties flattened = processor.getFlattenedProperties();
+				for (Iterator<Object> i = flattened.keySet().iterator(); i.hasNext();) {
+					String key = (String) i.next();
+					if (project.getProperty(key) == null) {
+						project.setProperty(key, flattened.getProperty(key));
+					}
+				}
+			}
+			report();
+		} catch (IOException e) {
+			e.printStackTrace();
+			throw new BuildException(e);
+		}
+	}
+
+	public void setPropertyFile(File file) {
+		this.propertyFile = file;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/PrepareTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/PrepareTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/PrepareTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,92 @@
+package aQute.bnd.ant;
+
+/**
+ * The idea of this task is to read all the properties as if bnd has read them.
+ * This makes it easier to use bnd standalone on the same data.
+ */
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.build.Project;
+
+public class PrepareTask extends BaseTask {
+    File    basedir;
+    boolean print = false;
+    String    top;
+
+    public void execute() throws BuildException {
+        try {
+            if (basedir == null || !basedir.isDirectory())
+                throw new BuildException("The given base dir does not exist "
+                        + basedir);
+
+            Project project = Workspace.getProject(basedir);
+            project.setProperty("in.ant", "true");
+            project.setProperty("environment", "ant");
+            
+            // Check if we are in a sub build, in that case
+            // top will be set to the target directory at the
+            // top project.
+            if ( top!=null && top.length()>0 && !top.startsWith("$"))
+                project.setProperty("top", top);
+            
+            project.setExceptions(true);
+            Properties properties = project.getFlattenedProperties();
+            if (report() || report(project))
+                throw new BuildException(
+                        "Errors during Eclipse Path inspection");
+
+            copyProperties(properties);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new BuildException(e);
+        }
+    }
+
+    private void copyProperties(Properties flattened) {
+        for (Enumeration<?> k = flattened.propertyNames(); k.hasMoreElements();) {
+            String key = (String) k.nextElement();
+            String value = flattened.getProperty(key);
+            if (isPrint())
+                System.out.printf("%-20s = %s\n", key, value);
+
+            // We override existing values.
+            getProject().setProperty(key, value);
+        }
+    }
+
+    public boolean isPrint() {
+        return print;
+    }
+
+    /**
+     * Print out the properties when they are set in sorted order
+     * 
+     * @param print
+     */
+    public void setPrint(boolean print) {
+        this.print = print;
+    }
+
+    /**
+     * Set the base directory of the project. This property MUST be set.
+     * 
+     * @param basedir
+     */
+    public void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+    
+    /**
+     * Set the base directory of the project. This property MUST be set.
+     * 
+     * @param basedir
+     */
+    public void setTop(String top) {
+        this.top = top;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ProjectTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ProjectTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/ProjectTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,47 @@
+package aQute.bnd.ant;
+
+/**
+ * The idea of this task is to read all the properties as if bnd has read them.
+ * This makes it easier to use bnd standalone on the same data.
+ */
+
+import java.io.*;
+
+import org.apache.tools.ant.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.build.Project;
+
+public class ProjectTask extends BaseTask {
+    File basedir;
+    boolean underTest;
+    
+    public void execute() throws BuildException {
+        try {
+            if (basedir == null || !basedir.isDirectory())
+                throw new BuildException("The given base dir does not exist "
+                        + basedir);
+
+            Project project = Workspace.getProject(basedir);
+            project.build(underTest);
+            report(project);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Set the base directory of the project. This property MUST be set.
+     * 
+     * @param basedir
+     */
+    public void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+    
+    
+    public void setUnderTest(boolean underTest) {
+        this.underTest = underTest;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/WrapTask.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/WrapTask.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/ant/WrapTask.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,109 @@
+
+package aQute.bnd.ant;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import aQute.bnd.main.*;
+import aQute.libg.qtokens.*;
+import aQute.libg.reporter.*;
+
+public class WrapTask extends BaseTask implements Reporter {
+	List<File>	jars		= new ArrayList<File>();
+	File	output		= null;
+	File	definitions = null;
+	List<File>	classpath	= new ArrayList<File>();
+	
+	boolean	failok;
+	boolean	exceptions;
+	boolean	print;
+
+	@SuppressWarnings("unchecked")
+    public void execute() throws BuildException {
+		boolean failed = false;
+		
+		try {
+			if (jars == null)
+				throw new BuildException("No files set");
+
+			if (output == null)
+				output = getProject().getBaseDir();
+
+			if (definitions == null)
+				definitions = getProject().getBaseDir();
+			
+			for (Iterator<File> f = jars.iterator(); f.hasNext();) {
+				bnd bnd = new bnd();
+				bnd.setPedantic(isPedantic());
+				File file = f.next();
+				String name = file.getName();
+				name = name.replaceFirst("(\\.jar)?$", ".bnd");
+				File bndFile = new File(definitions, name );
+				bnd.doWrap(bndFile.exists()?bndFile:null, file, output, null, 0, getProject().getProperties());
+				failed |= report(bnd);
+			}
+		} catch (Exception e) {
+			if (exceptions)
+				e.printStackTrace();
+			if (!failok)
+				throw new BuildException("Failed to build jar file: " + e, e);
+		}
+		if ( failed && !failok)
+			throw new BuildException("Failed to wrap jar file");
+	}
+
+	public void setJars(String files) {
+		addAll(this.jars, files, ",");
+	}
+
+	void addAll(List<File> list, String files, String separator) {
+		QuotedTokenizer qt = new QuotedTokenizer(files, separator);
+		String entries[] = qt.getTokens();
+		File project = getProject().getBaseDir();
+		for (int i = 0; i < entries.length; i++) {
+			File f = getFile(project, entries[i]);
+			if (f.exists())
+				list.add(f);
+			else
+				error("Can not find bnd file to process: "
+						+ f.getAbsolutePath());
+		}
+	}
+
+	public void setClasspath(String files) {
+		addAll(classpath, files, File.pathSeparator+",");
+	}
+
+	boolean isFailok() {
+		return failok;
+	}
+
+	public void setFailok(boolean failok) {
+		this.failok = failok;
+	}
+
+	public void setExceptions(boolean exceptions) {
+		this.exceptions = exceptions;
+	}
+
+
+	public void setOutput(File output) {
+		this.output = output;
+	}
+	
+	public void setDefinitions(File out) {
+		definitions = out;
+	}
+	
+	public void addConfiguredFileSet(FileSet list) {
+		DirectoryScanner scanner = list.getDirectoryScanner(getProject());
+		String files[] = scanner.getIncludedFiles();
+		for (int i = 0; i < files.length; i++) {
+			File f= getFile(scanner.getBasedir(), files[i]);
+			this.jars.add(f);
+		}
+	}	
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/CircularDependencyException.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/CircularDependencyException.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/CircularDependencyException.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,10 @@
+package aQute.bnd.build;
+
+public class CircularDependencyException extends Exception {
+    public CircularDependencyException(String string) {
+        super(string);
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Container.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Container.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Container.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,131 @@
+package aQute.bnd.build;
+
+import java.io.*;
+import java.util.*;
+
+public class Container {
+    public enum TYPE {
+        REPO, PROJECT, EXTERNAL, LIBRARY, ERROR
+    }
+
+    final File                file;
+    final TYPE                type;
+    final String              bsn;
+    final String              version;
+    final String              error;
+    final Project             project;
+    final Map<String, String> attributes;
+
+    Container(Project project, String bsn, String version, TYPE type,
+            File source, String error, Map<String, String> attributes) {
+        this.bsn = bsn;
+        this.version = version;
+        this.type = type;
+        this.file = source != null ? source : new File("/" + bsn + ":"
+                + version + ":" + type);
+        this.project = project;
+        this.error = error;
+        if (attributes == null || attributes.isEmpty())
+            this.attributes = Collections.emptyMap();
+        else
+            this.attributes = attributes;
+    }
+
+    public Container(Project project, File file) {
+        this(project, file.getName(), "project", TYPE.PROJECT, file, null, null);
+    }
+
+    public Container(File file) {
+        this(null, file.getName(), "project", TYPE.EXTERNAL, file, null, null);
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public String getBundleSymbolicName() {
+        return bsn;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public TYPE getType() {
+        return type;
+    }
+
+    public String getError() {
+        return error;
+    }
+
+    public boolean equals(Object other) {
+        if (other instanceof Container)
+            return file.equals(((Container) other).file);
+        else
+            return false;
+    }
+
+    public int hashCode() {
+        return file.hashCode();
+    }
+
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Must show the file name or the error formatted as a file name
+     * 
+     * @return
+     */
+    public String toString() {
+        if (getError() != null)
+            return "/error/" + getError();
+        else
+            return getFile().getAbsolutePath();
+    }
+
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Return the this if this is anything else but a library. If it is a
+     * library, return the members. This could work recursively, e.g., libraries
+     * can point to libraries.
+     * 
+     * @return
+     * @throws Exception
+     */
+    public List<Container> getMembers() throws Exception {
+        List<Container> result = project.newList();
+
+        // Are ww a library? If no, we are the result
+        if (getType() != TYPE.LIBRARY)
+            result.add(this);
+        else {
+            // We are a library, parse the file. This is
+            // basically a specification clause per line.
+            // I.e. you can do bsn; version, bsn2; version. But also
+            // spread it out over lines.
+            InputStream in = new FileInputStream(file);
+            BufferedReader rd = new BufferedReader(new InputStreamReader(in,
+                    "UTF-8"));
+            try {
+                String line;
+                while ((line = rd.readLine()) != null) {
+                    line = line.trim();
+                    if (!line.startsWith("#") && line.length() > 0) {
+                        List<Container> list = project.getBundles(
+                                Workspace.STRATEGY_EXACT, line);
+                        result.addAll(list);
+                    }
+                }
+            } finally {
+                in.close();
+            }
+        }
+        return result;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Project.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Project.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Project.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1192 @@
+package aQute.bnd.build;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.bnd.help.*;
+import aQute.bnd.service.*;
+import aQute.bnd.service.action.*;
+import aQute.bnd.test.*;
+import aQute.lib.osgi.*;
+import aQute.lib.osgi.eclipse.*;
+import aQute.libg.sed.*;
+import aQute.service.scripting.*;
+
+/**
+ * This class is NOT threadsafe
+ * 
+ * @author aqute
+ * 
+ */
+public class Project extends Processor {
+
+    final static String         DEFAULT_ACTIONS = "build; label='Build', test; label='Test', clean; label='Clean', release; label='Release', refreshAll; label=Refresh";
+    public final static String  BNDFILE         = "bnd.bnd";
+    public final static String  BNDCNF          = "cnf";
+    final Workspace             workspace;
+    boolean                     preparedPaths;
+    final Collection<Project>   dependson       = new LinkedHashSet<Project>();
+    final Collection<Container> buildpath       = new LinkedHashSet<Container>();
+    final Collection<Container> runpath         = new LinkedHashSet<Container>();
+    final Collection<File>      sourcepath      = new LinkedHashSet<File>();
+    final Collection<File>      allsourcepath   = new LinkedHashSet<File>();
+    final Collection<Container> bootclasspath   = new LinkedHashSet<Container>();
+    final Collection<Container> runbundles      = new LinkedHashSet<Container>();
+    File                        output;
+    File                        target;
+    boolean                     inPrepare;
+    int                         revision;
+    File                        files[];
+
+    public Project(Workspace workspace, File projectDir, File buildFile)
+            throws Exception {
+        super(workspace);
+        this.workspace = workspace;
+        setFileMustExist(false);
+        setProperties(buildFile);
+        assert workspace != null;
+        // For backward compatibility reasons, we also read
+        readBuildProperties();
+    }
+
+    public Project(Workspace workspace, File buildDir) throws Exception {
+        this(workspace, buildDir, new File(buildDir, BNDFILE));
+    }
+
+    private void readBuildProperties() throws Exception {
+        try {
+            File f = getFile("build.properties");
+            if (f.isFile()) {
+                Properties p = loadProperties(f);
+                for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
+                    String key = (String) e.nextElement();
+                    String newkey = key;
+                    if (key.indexOf('$') >= 0) {
+                        newkey = getReplacer().process(key);
+                    }
+                    setProperty(newkey, p.getProperty(key));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static Project getUnparented(File propertiesFile) throws Exception {
+        propertiesFile = propertiesFile.getAbsoluteFile();
+        Workspace workspace = new Workspace(propertiesFile.getParentFile());
+        Project project = new Project(workspace, propertiesFile.getParentFile());
+        project.setProperties(propertiesFile);
+        project.setFileMustExist(true);
+        return project;
+    }
+
+    public synchronized boolean isValid() {
+        return getBase().isDirectory() && getPropertiesFile().isFile();
+    }
+
+    /**
+     * Return a new builder that is nicely setup for this project. Please close
+     * this builder after use.
+     * 
+     * @param parent
+     *            The project builder to use as parent, use this project if null
+     * @return
+     * @throws Exception
+     */
+    public synchronized ProjectBuilder getBuilder(ProjectBuilder parent)
+            throws Exception {
+
+        ProjectBuilder builder;
+
+        if (parent == null)
+            builder = new ProjectBuilder(this);
+        else
+            builder = new ProjectBuilder(parent);
+
+        builder.setBase(getBase());
+
+        for (Container file : getBuildpath()) {
+            builder.addClasspath(file.getFile());
+        }
+
+        for (Container file : getBootclasspath()) {
+            builder.addClasspath(file.getFile());
+        }
+
+        for (File file : getAllsourcepath()) {
+            builder.addSourcepath(file);
+        }
+        return builder;
+    }
+
+    public synchronized int getChanged() {
+        return revision;
+    }
+
+    /*
+     * Indicate a change in the external world that affects our build. This will
+     * clear any cached results.
+     */
+    public synchronized void setChanged() {
+        // if (refresh()) {
+        preparedPaths = false;
+        files = null;
+        revision++;
+        // }
+    }
+
+    public Workspace getWorkspace() {
+        return workspace;
+    }
+
+    public String toString() {
+        return getBase().getName();
+    }
+
+    /**
+     * Set up all the paths
+     */
+
+    public synchronized void prepare() throws Exception {
+        if (inPrepare)
+            throw new CircularDependencyException(toString());
+        if (!preparedPaths) {
+            inPrepare = true;
+            try {
+                dependson.clear();
+                buildpath.clear();
+                sourcepath.clear();
+                allsourcepath.clear();
+                bootclasspath.clear();
+                runpath.clear();
+                runbundles.clear();
+
+                // We use a builder to construct all the properties for
+                // use.
+                setProperty("basedir", getBase().getAbsolutePath());
+
+                // If a bnd.bnd file exists, we read it.
+                // Otherwise, we just do the build properties.
+                if (!getPropertiesFile().isFile()
+                        && new File(getBase(), ".classpath").isFile()) {
+                    // Get our Eclipse info, we might depend on other projects
+                    // though ideally this should become empty and void
+                    doEclipseClasspath();
+                }
+
+                // Calculate our source directory
+
+                File src = getSrc();
+                if (src.isDirectory()) {
+                    sourcepath.add(src);
+                    allsourcepath.add(src);
+                } else
+                    sourcepath.add(getBase());
+
+                // Set default bin directory
+                output = getFile(getProperty("bin", "bin")).getAbsoluteFile();
+                if (output.isDirectory()) {
+                    if (!buildpath.contains(output))
+                        buildpath.add(new Container(this, output));
+                } else {
+                    if (!output.exists())
+                        output.mkdirs();
+                    if (!output.isDirectory())
+                        error("Can not find output directory: " + output);
+                }
+
+                // Where we store all our generated stuff.
+                target = getFile(getProperty("target", "generated"));
+
+                // We might have some other projects we want build
+                // before we do anything, but these projects are not in
+                // our path. The -dependson allows you to build them before.
+
+                List<Project> dependencies = new ArrayList<Project>();
+                // dependencies.add( getWorkspace().getProject("cnf"));
+
+                String dp = getProperty(Constants.DEPENDSON);
+                Set<String> requiredProjectNames = parseHeader(dp).keySet();
+                List<DependencyContributor> dcs = getPlugins(DependencyContributor.class);
+                for (DependencyContributor dc : dcs)
+                    dc.addDependencies(this, requiredProjectNames);
+
+                for (String p : requiredProjectNames) {
+                    Project required = getWorkspace().getProject(p);
+                    if (required == null)
+                        error("No such project " + required + " on "
+                                + Constants.DEPENDSON);
+                    else {
+                        dependencies.add(required);
+                    }
+
+                }
+
+                // We have two paths that consists of repo files, projects,
+                // or some other stuff. The doPath routine adds them to the
+                // path and extracts the projects so we can build them before.
+
+                doPath(buildpath, dependencies, parseBuildpath(), bootclasspath);
+                doPath(runpath, dependencies, parseTestpath(), bootclasspath);
+                doPath(runbundles, dependencies, parseTestbundles(), null);
+
+                // We now know all dependend projects. But we also depend
+                // on whatever those projects depend on. This creates an
+                // ordered list without any duplicates. This of course assumes
+                // that there is no circularity. However, this is checked
+                // by the inPrepare flag, will throw an exception if we
+                // are circular.
+
+                Set<Project> done = new HashSet<Project>();
+                done.add(this);
+                allsourcepath.addAll(sourcepath);
+
+                for (Project project : dependencies)
+                    project.traverse(dependson, done);
+
+                for (Project project : dependson) {
+                    allsourcepath.addAll(project.getSourcepath());
+                }
+                if (isOk())
+                    preparedPaths = true;
+            } finally {
+                inPrepare = false;
+            }
+        }
+    }
+
+    public File getSrc() {
+        return new File(getBase(), getProperty("src", "src"));
+    }
+
+    private void traverse(Collection<Project> dependencies, Set<Project> visited)
+            throws Exception {
+        if (visited.contains(this))
+            return;
+
+        visited.add(this);
+
+        for (Project project : getDependson())
+            project.traverse(dependencies, visited);
+
+        dependencies.add(this);
+    }
+
+    /**
+     * Iterate over the entries and place the projects on the projects list and
+     * all the files of the entries on the resultpath.
+     * 
+     * @param resultpath
+     *            The list that gets all the files
+     * @param projects
+     *            The list that gets any projects that are entries
+     * @param entries
+     *            The input list of classpath entries
+     */
+    private void doPath(Collection<Container> resultpath,
+            Collection<Project> projects, Collection<Container> entries,
+            Collection<Container> bootclasspath) {
+        for (Container cpe : entries) {
+            if (cpe.getError() != null)
+                error(cpe.getError());
+            else {
+                if (cpe.getType() == Container.TYPE.PROJECT) {
+                    projects.add(cpe.getProject());
+                }
+                if (bootclasspath != null
+                        && cpe.getBundleSymbolicName().startsWith("ee.")
+                        || cpe.getAttributes().containsKey("boot"))
+                    bootclasspath.add(cpe);
+                else
+                    resultpath.add(cpe);
+            }
+        }
+    }
+
+    /**
+     * Parse the list of bundles that are a prerequisite to this project.
+     * 
+     * Bundles are listed in repo specific names. So we just let our repo
+     * plugins iterate over the list of bundles and we get the highest version
+     * from them.
+     * 
+     * @return
+     */
+
+    private List<Container> parseBuildpath() throws Exception {
+        return getBundles(Constants.STRATEGY_LOWEST,
+                getProperty(Constants.BUILDPATH));
+    }
+
+    private List<Container> parseTestpath() throws Exception {
+        return getBundles(Constants.STRATEGY_HIGHEST,
+                getProperty(Constants.RUNPATH));
+    }
+
+    private List<Container> parseTestbundles() throws Exception {
+        return getBundles(Constants.STRATEGY_HIGHEST,
+                getProperty(Constants.RUNBUNDLES));
+    }
+
+    /**
+     * Analyze the header and return a list of files that should be on the
+     * build, test or some other path. The list is assumed to be a list of bsns
+     * with a version specification. The special case of version=project
+     * indicates there is a project in the same workspace. The path to the
+     * output directory is calculated. The default directory ${bin} can be
+     * overridden with the output attribute.
+     * 
+     * @param strategy
+     *            STRATEGY_LOWEST or STRATEGY_HIGHEST
+     * @param spec
+     *            The header
+     * @return
+     */
+    public List<Container> getBundles(int strategy, String spec)
+            throws Exception {
+        List<Container> result = new ArrayList<Container>();
+        Map<String, Map<String, String>> bundles = parseHeader(spec);
+
+        try {
+            for (Iterator<Map.Entry<String, Map<String, String>>> i = bundles
+                    .entrySet().iterator(); i.hasNext();) {
+                Map.Entry<String, Map<String, String>> entry = i.next();
+                String bsn = entry.getKey();
+                Map<String, String> attrs = entry.getValue();
+
+                Container found = null;
+
+                String versionRange = attrs.get("version");
+
+                if (versionRange != null && versionRange.equals("latest")) {
+                    found = getBundle(bsn, versionRange, strategy, attrs);
+                }
+                if (found == null) {
+                    if (versionRange != null && (versionRange.equals("project") || versionRange.equals("latest"))) {
+                        Project project = getWorkspace().getProject(bsn);
+                        if (project.exists()) {
+                            File f = project.getOutput();
+                            found = new Container(project, bsn, "project",
+                                    Container.TYPE.PROJECT, f, null, attrs);
+                        } else {
+                            error(
+                                    "Reference to project that does not exist in workspace\n"
+                                            + "  Project       %s\n"
+                                            + "  Specification %s", bsn, spec);
+                            continue;
+                        }
+                    } else if (versionRange != null
+                            && versionRange.equals("file")) {
+                        File f = getFile(bsn);
+                        String error = null;
+                        if (!f.exists())
+                            error = "File does not exist";
+                        if (f.getName().endsWith(".lib")) {
+                            found = new Container(this, bsn, "file",
+                                    Container.TYPE.LIBRARY, f, error, attrs);
+                        } else {
+                            found = new Container(this, bsn, "file",
+                                    Container.TYPE.EXTERNAL, f, error, attrs);
+                        }
+                    } else {
+                        found = getBundle(bsn, versionRange, strategy, attrs);
+                    }
+                }
+                if (found != null) {
+                    List<Container> libs = found.getMembers();
+                    for (Container cc : libs) {
+                        if (result.contains(cc))
+                            warning("Multiple bundles with the same final URL: "
+                                    + cc);
+
+                        result.add(cc);
+                    }
+                } else {
+                    // Oops, not a bundle in sight :-(
+                    Container x = new Container(this, bsn, versionRange,
+                            Container.TYPE.ERROR, null, bsn + ";version="
+                                    + versionRange + " not found", attrs);
+                    result.add(x);
+                    warning("Can not find URL for bsn " + bsn);
+                }
+            }
+        } catch (Exception e) {
+            error("While tring to get the bundles from " + spec, e);
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    public Collection<Project> getDependson() throws Exception {
+        prepare();
+        return dependson;
+    }
+
+    public Collection<Container> getBuildpath() throws Exception {
+        prepare();
+        return buildpath;
+    }
+
+    public Collection<Container> getRunpath() throws Exception {
+        prepare();
+        return runpath;
+    }
+
+    public Collection<Container> getRunbundles() throws Exception {
+        prepare();
+        return runbundles;
+    }
+
+    public Collection<File> getSourcepath() throws Exception {
+        prepare();
+        return sourcepath;
+    }
+
+    public Collection<File> getAllsourcepath() throws Exception {
+        prepare();
+        return allsourcepath;
+    }
+
+    public Collection<Container> getBootclasspath() throws Exception {
+        prepare();
+        return bootclasspath;
+    }
+
+    public File getOutput() throws Exception {
+        prepare();
+        return output;
+    }
+
+    private void doEclipseClasspath() throws Exception {
+        EclipseClasspath eclipse = new EclipseClasspath(this, getWorkspace()
+                .getBase(), getBase());
+        eclipse.setRecurse(false);
+
+        // We get the file directories but in this case we need
+        // to tell ant that the project names
+        for (File dependent : eclipse.getDependents()) {
+            Project required = workspace.getProject(dependent.getName());
+            dependson.add(required);
+        }
+        for (File f : eclipse.getClasspath()) {
+            buildpath.add(new Container(f));
+        }
+        for (File f : eclipse.getBootclasspath()) {
+            bootclasspath.add(new Container(f));
+        }
+        sourcepath.addAll(eclipse.getSourcepath());
+        allsourcepath.addAll(eclipse.getAllSources());
+        output = eclipse.getOutput();
+    }
+
+    public String _p_dependson(String args[]) throws Exception {
+        return list(args, toFiles(getDependson()));
+    }
+
+    private Collection<?> toFiles(Collection<Project> projects) {
+        List<File> files = new ArrayList<File>();
+        for (Project p : projects) {
+            files.add(p.getBase());
+        }
+        return files;
+    }
+
+    public String _p_buildpath(String args[]) throws Exception {
+        return list(args, getBuildpath());
+    }
+
+    public String _p_testpath(String args[]) throws Exception {
+        return list(args, getRunpath());
+    }
+
+    public String _p_sourcepath(String args[]) throws Exception {
+        return list(args, getSourcepath());
+    }
+
+    public String _p_allsourcepath(String args[]) throws Exception {
+        return list(args, getAllsourcepath());
+    }
+
+    public String _p_bootclasspath(String args[]) throws Exception {
+        return list(args, getBootclasspath());
+    }
+
+    public String _p_output(String args[]) throws Exception {
+        if (args.length != 1)
+            throw new IllegalArgumentException(
+                    "${output} should not have arguments");
+        return getOutput().getAbsolutePath();
+    }
+
+    private String list(String[] args, Collection<?> list) {
+        if (args.length > 3)
+            throw new IllegalArgumentException(
+                    "${"
+                            + args[0]
+                            + "[;<separator>]} can only take a separator as argument, has "
+                            + Arrays.toString(args));
+
+        String separator = ",";
+        if (args.length == 2) {
+            separator = args[1];
+        }
+
+        return join(list, separator);
+    }
+
+    protected Object[] getMacroDomains() {
+        return new Object[] { workspace };
+    }
+
+    public File release(Jar jar) throws Exception {
+        String name = getProperty(Constants.RELEASEREPO);
+        return release(name, jar);
+    }
+
+    /**
+     * Release
+     * 
+     * @param name
+     *            The repository name
+     * @param jar
+     * @return
+     * @throws Exception
+     */
+    public File release(String name, Jar jar) throws Exception {
+        List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
+        RepositoryPlugin rp = null;
+        for (RepositoryPlugin plugin : plugins) {
+            if (!plugin.canWrite()) {
+                continue;
+            }
+            if (name == null) {
+                rp = plugin;
+                break;
+            } else if (name.equals(plugin.getName())) {
+                rp = plugin;
+                break;
+            }
+        }
+
+        if (rp != null) {
+            try {
+                return rp.put(jar);
+            } catch (Exception e) {
+                error("Deploying " + jar.getName() + " on " + rp.getName(), e);
+            } finally {
+                jar.close();
+            }
+        }
+        return null;
+
+    }
+
+    public void release(boolean test) throws Exception {
+        String name = getProperty(Constants.RELEASEREPO);
+        release(name, test);
+    }
+
+    /**
+     * Release
+     * 
+     * @param name
+     *            The respository name
+     * @param test
+     *            Run testcases
+     * @throws Exception
+     */
+    public void release(String name, boolean test) throws Exception {
+        File[] jars = build(test);
+        // If build fails jars will be null
+        if (jars == null) {
+            return;
+        }
+        for (File jar : jars) {
+            Jar j = new Jar(jar);
+            release(name, j);
+            j.close();
+        }
+
+    }
+
+    /**
+     * Get a bundle from one of the plugin repositories.
+     * 
+     * @param bsn
+     *            The bundle symbolic name
+     * @param range
+     *            The version range
+     * @param lowest
+     *            set to LOWEST or HIGHEST
+     * @return the file object that points to the bundle or null if not found
+     * @throws Exception
+     *             when something goes wrong
+     */
+    public Container getBundle(String bsn, String range, int strategy,
+            Map<String, String> attrs) throws Exception {
+        List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
+
+        // If someone really wants the latest, lets give it to them.
+        // regardless of they asked for a lowest strategy
+        if (range != null && range.equals("latest"))
+            strategy = STRATEGY_HIGHEST;
+
+        for (RepositoryPlugin plugin : plugins) {
+            File[] results = plugin.get(bsn, range);
+            if (results != null && results.length > 0) {
+                File f = results[strategy == STRATEGY_LOWEST ? 0
+                        : results.length - 1];
+
+                if (f.getName().endsWith("lib"))
+                    return new Container(this, bsn, range,
+                            Container.TYPE.LIBRARY, f, null, attrs);
+                else
+                    return new Container(this, bsn, range, Container.TYPE.REPO,
+                            f, null, attrs);
+            }
+        }
+
+        return new Container(this, bsn, range, Container.TYPE.ERROR, null, bsn
+                + ";version=" + range + " Not found in " + plugins, null);
+    }
+
+    /**
+     * Deploy the file (which must be a bundle) into the repository.
+     * 
+     * @param name
+     *            The repository name
+     * @param file
+     *            bundle
+     */
+    public void deploy(String name, File file) throws Exception {
+        List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
+        RepositoryPlugin rp = null;
+        for (RepositoryPlugin plugin : plugins) {
+            if (!plugin.canWrite()) {
+                continue;
+            }
+            if (name == null) {
+                rp = plugin;
+                break;
+            } else if (name.equals(plugin.getName())) {
+                rp = plugin;
+                break;
+            }
+        }
+
+        if (rp != null) {
+            Jar jar = new Jar(file);
+            try {
+                rp.put(jar);
+                return;
+            } catch (Exception e) {
+                error("Deploying " + file + " on " + rp.getName(), e);
+            } finally {
+                jar.close();
+            }
+            return;
+        }
+        trace("No repo found " + file);
+        throw new IllegalArgumentException("No repository found for " + file);
+    }
+
+    /**
+     * Deploy the file (which must be a bundle) into the repository.
+     * 
+     * @param file
+     *            bundle
+     */
+    public void deploy(File file) throws Exception {
+        String name = getProperty(Constants.DEPLOYREPO);
+        deploy(name, file);
+    }
+
+    /**
+     * Macro access to the repository
+     * 
+     * ${repo;<bsn>[;<version>[;<low|high>]]}
+     */
+
+    public String _repo(String args[]) throws Exception {
+        if (args.length < 2)
+            throw new IllegalArgumentException(
+                    "Too few arguments for repo, syntax=: ${repo ';'<bsn> [ ; <version> ]}");
+
+        String bsns = args[1];
+        String version = null;
+        int strategy = Constants.STRATEGY_HIGHEST;
+
+        if (args.length > 2) {
+            version = args[2];
+            if (args.length == 4) {
+                if (args[3].equalsIgnoreCase("HIGHEST"))
+                    strategy = Constants.STRATEGY_HIGHEST;
+                else if (args[3].equalsIgnoreCase("LOWEST"))
+                    strategy = STRATEGY_LOWEST;
+                else
+                    error("${repo;<bsn>;<version>;<'highest'|'lowest'>} macro requires a strategy of 'highest' or 'lowest', and is "
+                            + args[3]);
+            }
+        }
+
+        Collection<String> parts = split(bsns);
+        List<String> paths = new ArrayList<String>();
+
+        for (String bsn : parts) {
+            Container jar = getBundle(bsn, version, strategy, null);
+            if (jar.getError() == null) {
+                paths.add(jar.getFile().getAbsolutePath());
+            } else {
+                error("The ${repo} macro could not find " + bsn
+                        + " in the repo, because " + jar.getError() + "\n"
+                        + "Repositories     : "
+                        + getPlugins(RepositoryPlugin.class) + "\n"
+                        + "Strategy         : " + strategy + "\n"
+                        + "Bsn              : " + bsn + ";version=" + version);
+            }
+        }
+        return join(paths);
+    }
+
+    public File getTarget() throws Exception {
+        prepare();
+        return target;
+    }
+
+    /**
+     * This is the external method that will pre-build any dependencies if it is
+     * out of date.
+     * 
+     * @param underTest
+     * @return
+     * @throws Exception
+     */
+    public File[] build(boolean underTest) throws Exception {
+        if (isUptodate())
+            return files;
+
+        return buildLocal(underTest);
+    }
+
+    /**
+     * This method must only be called when it is sure that the project has been
+     * build before in the same session.
+     * 
+     * It is a bit yucky, but ant creates different class spaces which makes it
+     * hard to detect we already build it.
+     * 
+     * @return
+     */
+
+    public File[] getBuildFiles() throws Exception {
+        File f = new File(getTarget(), BUILDFILES);
+        if (f.isFile()) {
+            FileReader fin = new FileReader(f);
+            BufferedReader rdr = new BufferedReader(fin);
+            try {
+                List<File> files = newList();
+                for (String s = rdr.readLine(); s != null; s = rdr.readLine()) {
+                    s = s.trim();
+                    File ff = new File(s);
+                    if (!ff.isFile()) {
+                        error(
+                                "buildfile lists file but the file does not exist %s",
+                                ff);
+                    } else
+                        files.add(ff);
+                }
+                return files.toArray(new File[files.size()]);
+            } finally {
+                fin.close();
+            }
+        }
+        return buildLocal(false);
+    }
+
+    /**
+     * Build without doing any dependency checking. Make sure any dependent
+     * projects are built first.
+     * 
+     * @param underTest
+     * @return
+     * @throws Exception
+     */
+    public File[] buildLocal(boolean underTest) throws Exception {
+        System.out.println("Building " + this);
+        File bfs = new File(getTarget(), BUILDFILES);
+        bfs.delete();
+
+        files = null;
+        ProjectBuilder builder = getBuilder(null);
+        if (underTest)
+            builder.setProperty(Constants.UNDERTEST, "true");
+        Jar jars[] = builder.builds();
+        File[] files = new File[jars.length];
+
+        File target = getTarget();
+        target.mkdirs();
+
+        for (int i = 0; i < jars.length; i++) {
+            Jar jar = jars[i];
+            try {
+                String bsn = jar.getName();
+                files[i] = new File(target, bsn + ".jar");
+                String msg = "";
+                if (!files[i].exists()
+                        || files[i].lastModified() < jar.lastModified()) {
+                    reportNewer(files[i].lastModified(), jar);
+                    files[i].delete();
+                    jar.write(files[i]);
+                } else {
+                    msg = "(not modified since "
+                            + new Date(files[i].lastModified()) + ")";
+                }
+                trace(jar.getName() + " (" + files[i].getName() + ") "
+                        + jar.getResources().size() + " " + msg);
+            } finally {
+                jar.close();
+            }
+        }
+        getInfo(builder);
+        builder.close();
+        if (isOk()) {
+            this.files = files;
+
+            // Write out the filenames in the buildfiles file
+            // so we can get them later evenin another process
+            FileWriter fw = new FileWriter(bfs);
+            try {
+                for (File f : files) {
+                    fw.append(f.getAbsolutePath());
+                    fw.append("\n");
+                }
+            } finally {
+                fw.close();
+            }
+            return files;
+        } else
+            return null;
+    }
+
+    private boolean isUptodate() throws Exception {
+        if (files == null) {
+            return false;
+        }
+
+        for (Project project : getDependson())
+            if (!project.isUptodate()) {
+                return false;
+            }
+
+        return true;
+    }
+
+    private void reportNewer(long lastModified, Jar jar) {
+        if (isTrue(getProperty(Constants.REPORTNEWER))) {
+            StringBuilder sb = new StringBuilder();
+            String del = "Newer than " + new Date(lastModified);
+            for (Map.Entry<String, Resource> entry : jar.getResources()
+                    .entrySet()) {
+                if (entry.getValue().lastModified() > lastModified) {
+                    sb.append(del);
+                    del = ", \n     ";
+                    sb.append(entry.getKey());
+                }
+            }
+            if (sb.length() > 0)
+                warning(sb.toString());
+        }
+    }
+
+    /**
+     * Refresh if we are based on stale data. This also implies our workspace.
+     */
+    public boolean refresh() {
+        boolean changed = false;
+        if (isCnf()) {
+            changed = workspace.refresh();
+        }
+        return super.refresh() || changed;
+    }
+
+    public boolean isCnf() {
+        return getBase().getName().equals(Workspace.CNFDIR);
+    }
+
+    public void propertiesChanged() {
+        super.propertiesChanged();
+        preparedPaths = false;
+    }
+
+    public String getName() {
+        return getBase().getName();
+    }
+
+    public Map<String, Action> getActions() {
+        Map<String, Action> all = newMap();
+        Map<String, Action> actions = newMap();
+        fillActions(all);
+        getWorkspace().fillActions(all);
+
+        for (Map.Entry<String, Action> action : all.entrySet()) {
+            String key = getReplacer().process(action.getKey());
+            if (key != null && key.trim().length() != 0)
+                actions.put(key, action.getValue());
+        }
+        return actions;
+    }
+
+    public void fillActions(Map<String, Action> all) {
+        List<NamedAction> plugins = getPlugins(NamedAction.class);
+        for (NamedAction a : plugins)
+            all.put(a.getName(), a);
+
+        Map<String, Map<String, String>> actions = parseHeader(getProperty(
+                "-actions", DEFAULT_ACTIONS));
+        for (Map.Entry<String, Map<String, String>> entry : actions.entrySet()) {
+            String key = Processor.removeDuplicateMarker(entry.getKey());
+            Action action;
+
+            if (entry.getValue().get("script") != null) {
+                // TODO check for the type
+                action = new ScriptAction(entry.getValue().get("type"), entry
+                        .getValue().get("script"));
+            } else {
+                action = new ReflectAction(key);
+            }
+            String label = entry.getValue().get("label");
+            all.put(label, action);
+        }
+    }
+
+    public void release() throws Exception {
+        release(false);
+    }
+
+    /**
+     * Release.
+     * 
+     * @param name
+     *            The repository name
+     * @throws Exception
+     */
+    public void release(String name) throws Exception {
+        release(name, false);
+    }
+
+    public void clean() throws Exception {
+        File target = getTarget();
+        if (target.isDirectory() && target.getParentFile() != null) {
+            delete(target);
+        }
+    }
+
+    public File[] build() throws Exception {
+        return build(false);
+    }
+
+    public boolean test() throws Exception {
+        boolean ok = true;
+        String testbundles = getProperty(TESTBUNDLES);
+
+        if (testbundles == null) {
+            File jars[] = build(true);
+            for (File jar : jars)
+                ok &= test(jar);
+
+        } else {
+            List<Container> containers = getBundles(STRATEGY_HIGHEST,
+                    testbundles);
+            for (Container container : containers) {
+                if (container.getError() == null) {
+                    File jar = container.getFile();
+                    ok &= test(jar);
+                } else
+                    error(container.getError());
+            }
+        }
+        return ok;
+    }
+
+    public boolean test(File f) throws Exception {
+        ProjectLauncher pl = new ProjectLauncher(this);
+        pl.setReport(getProperty("target") + "/"
+                + f.getName().replace(".jar", ".xml"));
+        int errors = pl.run(f);
+        getInfo(pl);
+        if (errors == 0) {
+            trace("ok");
+            return true;
+        } else {
+            error("Failed: " + normalize(f) + ", " + errors + " test"
+                    + (errors > 1 ? "s" : "") + " failures, see "
+                    + normalize(pl.getTestreport()));
+            return false;
+        }
+    }
+
+    private void delete(File target) {
+        if (target.getParentFile() == null)
+            throw new IllegalArgumentException("Can not delete root!");
+        if (!target.exists())
+            return;
+
+        if (target.isDirectory()) {
+            File sub[] = target.listFiles();
+            for (File s : sub)
+                delete(s);
+        }
+        target.delete();
+    }
+
+    /**
+     * This methods attempts to turn any jar into a valid jar. If this is a
+     * bundle with manifest, a manifest is added based on defaults. If it is a
+     * bundle, but not r4, we try to add the r4 headers.
+     * 
+     * @param name
+     * @param in
+     * @return
+     * @throws Exception
+     */
+    public Jar getValidJar(File f) throws Exception {
+        Jar jar = new Jar(f);
+        Manifest manifest = jar.getManifest();
+        if (manifest == null) {
+            trace("Wrapping with all defaults");
+            Builder b = new Builder(this);
+            b.addClasspath(jar);
+            b.setProperty("Bnd-Message", "Wrapped from " + f.getAbsolutePath()
+                    + "because lacked manifest");
+            b.setProperty(Constants.EXPORT_PACKAGE, "*");
+            b.setProperty(Constants.IMPORT_PACKAGE, "*;resolution:=optional");
+            jar = b.build();
+        } else if (manifest.getMainAttributes().getValue(
+                Constants.BUNDLE_MANIFESTVERSION) == null) {
+            trace("Not a release 4 bundle, wrapping with manifest as source");
+            Builder b = new Builder(this);
+            b.addClasspath(jar);
+            b.setProperty(Constants.PRIVATE_PACKAGE, "*");
+            b.mergeManifest(manifest);
+            String imprts = manifest.getMainAttributes().getValue(
+                    Constants.IMPORT_PACKAGE);
+            if (imprts == null)
+                imprts = "";
+            else
+                imprts += ",";
+            imprts += "*;resolution=optional";
+
+            b.setProperty(Constants.IMPORT_PACKAGE, imprts);
+            b.setProperty("Bnd-Message", "Wrapped from " + f.getAbsolutePath()
+                    + "because had incomplete manifest");
+            jar = b.build();
+        }
+        return jar;
+    }
+
+    public String _project(String args[]) {
+        return getBase().getAbsolutePath();
+    }
+
+    public void bump(String mask) throws IOException {
+        Sed sed = new Sed(getReplacer(), getPropertiesFile());
+        sed
+                .replace(
+                        "(Bundle-Version\\s*(:|=)\\s*)(([0-9]+(\\.[0-9]+(\\.[0-9]+)?)?))",
+                        "$1${version;" + mask + ";$3}");
+        sed.doIt();
+        refresh();
+    }
+
+    public void bump() throws IOException {
+        bump(getProperty(BUMPPOLICY, "=+0"));
+    }
+
+    public void action(String command) throws Exception {
+        Map<String, Action> actions = getActions();
+
+        Action a = actions.get(command);
+        if (a == null)
+            a = new ReflectAction(command);
+        a.execute(this, command);
+    }
+
+    public String _findfile(String args[]) {
+        File f = getFile(args[1]);
+        List<String> files = new ArrayList<String>();
+        tree(files, f, "", Instruction.getPattern(args[2]));
+        return join(files);
+    }
+
+    void tree(List<String> list, File current, String path, Instruction instr) {
+        if (path.length() > 0)
+            path = path + "/";
+
+        String subs[] = current.list();
+        if (subs != null) {
+            for (String sub : subs) {
+                File f = new File(current, sub);
+                if (f.isFile()) {
+                    if (instr.matches(sub) && !instr.isNegated())
+                        list.add(path + sub);
+                } else
+                    tree(list, f, path + sub, instr);
+            }
+        }
+    }
+
+    public void refreshAll() {
+        workspace.refresh();
+        refresh();
+    }
+
+    @SuppressWarnings("unchecked")
+    public void script(String type, String script) throws Exception {
+        // TODO check tyiping
+        List<Scripter> scripters = getPlugins(Scripter.class);
+        if (scripters.isEmpty()) {
+            error(
+                    "Can not execute script because there are no scripters registered: %s",
+                    script);
+            return;
+        }
+        Map x = (Map) getProperties();
+        scripters.get(0)
+                .eval((Map<String, Object>) x, new StringReader(script));
+    }
+
+    public String _repos(String args[]) throws Exception {
+        List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
+        List<String> names = new ArrayList<String>();
+        for (RepositoryPlugin rp : repos)
+            names.add(rp.getName());
+        return join(names, ", ");
+    }
+
+    public String _help(String args[]) throws Exception {
+        if (args.length == 1)
+            return "Specify the option or header you want information for";
+
+        Syntax syntax = Syntax.HELP.get(args[1]);
+        if (syntax == null)
+            return "No help for " + args[1];
+
+        String what = null;
+        if (args.length > 2)
+            what = args[2];
+
+        if (what == null || what.equals("lead"))
+            return syntax.getLead();
+        if (what == null || what.equals("example"))
+            return syntax.getExample();
+        if (what == null || what.equals("pattern"))
+            return syntax.getPattern();
+        if (what == null || what.equals("values"))
+            return syntax.getValues();
+
+        return "Invalid type specified for help: lead, example, pattern, values";
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ProjectBuilder.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ProjectBuilder.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ProjectBuilder.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,34 @@
+package aQute.bnd.build;
+
+import aQute.lib.osgi.*;
+
+ at SuppressWarnings("unchecked")
+public class ProjectBuilder extends Builder {
+    Project project;
+
+    public ProjectBuilder(Project project) {
+        super(project);
+        this.project = project;
+    }
+
+    public ProjectBuilder(ProjectBuilder builder) {
+        super(builder);
+        this.project = builder.project;
+    }
+
+
+    /** 
+     * We put our project and our workspace on the macro path.
+     */
+    protected Object [] getMacroDomains() {
+        return new Object[] {project, project.getWorkspace()};
+    }
+
+    public Builder getSubBuilder() throws Exception {
+        return project.getBuilder(this);
+    }
+
+    public Project getProject() {
+        return project;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ReflectAction.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ReflectAction.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ReflectAction.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,22 @@
+package aQute.bnd.build;
+
+import java.lang.reflect.*;
+
+import aQute.bnd.service.action.*;
+
+public class ReflectAction implements Action {
+    String  what;
+    
+    public ReflectAction(String what) {
+        this.what = what;
+    }
+    
+    public void execute(Project project, String action) throws Exception {
+        Method m = project.getClass().getMethod(what);
+        m.invoke(project);
+    }
+
+    public String toString() {
+        return "ra:" + what;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ScriptAction.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ScriptAction.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/ScriptAction.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,18 @@
+package aQute.bnd.build;
+
+import aQute.bnd.service.action.*;
+
+public class ScriptAction implements Action {
+    final String script;
+    final String type;
+    
+    public ScriptAction(String type, String script) {
+        this.script = script;
+        this.type = type;
+    }
+
+    public void execute(Project project, String action) throws Exception {
+        project.script(type, script);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Workspace.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Workspace.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/build/Workspace.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,109 @@
+package aQute.bnd.build;
+
+import java.io.*;
+import java.lang.ref.*;
+import java.util.*;
+
+import aQute.bnd.service.action.*;
+import aQute.lib.osgi.*;
+
+public class Workspace extends Processor {
+    public final static int                    STRATEGY_HIGHEST = 1;
+    public final static int                    STRATEGY_EXACT   = 0;
+    public final static int                    STRATEGY_LOWEST  = -1;
+    public static final String                 BUILDFILE        = "build.bnd";
+    public static final String                 CNFDIR           = "cnf";
+
+    static Map<File, WeakReference<Workspace>> cache            = newHashMap();
+    final Map<String, Project>                 models           = newHashMap();
+    final Map<String, Action>                  commands         = newMap();
+
+    /**
+     * This static method finds the workspace and creates a project (or returns
+     * an existing project)
+     * 
+     * @param projectDir
+     * @return
+     */
+    public static Project getProject(File projectDir) throws Exception {
+        projectDir = projectDir.getAbsoluteFile();
+        assert projectDir.isDirectory();
+
+        Workspace ws = getWorkspace(projectDir.getParentFile());
+        return ws.getProject(projectDir.getName());
+    }
+
+    public static Workspace getWorkspace(File workspaceDir) throws Exception {
+        workspaceDir = workspaceDir.getAbsoluteFile();
+        synchronized (cache) {
+            WeakReference<Workspace> wsr = cache.get(workspaceDir);
+            Workspace ws;
+            if (wsr == null || (ws = wsr.get()) == null) {
+                ws = new Workspace(workspaceDir);
+                cache.put(workspaceDir, new WeakReference<Workspace>(ws));
+            }
+            return ws;
+        }
+    }
+
+    public Workspace(File dir) throws Exception {
+        dir = dir.getAbsoluteFile();
+        dir.mkdirs();
+        assert dir.isDirectory();
+
+        File buildDir = new File(dir, CNFDIR).getAbsoluteFile();
+        File buildFile = new File(buildDir, BUILDFILE).getAbsoluteFile();
+        if (!buildFile.isFile())
+            warning("No Build File in " + dir);
+        setProperties(buildFile, dir);
+    }
+
+    public Project getProject(String bsn) throws Exception {
+        synchronized (models) {
+            Project project = models.get(bsn);
+            if (project != null)
+                return project;
+
+            File projectDir = getFile(bsn);
+            project = new Project(this, projectDir);
+            models.put(bsn, project);
+            return project;
+        }
+    }
+
+    public boolean isPresent(String name) {
+        return models.containsKey(name);
+    }
+
+    public Collection<Project> getCurrentProjects() {
+        return models.values();
+    }
+
+    public boolean refresh() {
+        if (super.refresh()) {
+            for (Project project : getCurrentProjects()) {
+                project.propertiesChanged();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public String _workspace(String args[]) {
+        return getBase().getAbsolutePath();
+    }
+
+    public void addCommand(String menu, Action action) {
+        commands.put(menu, action);
+    }
+
+    public void removeCommand(String menu) {
+        commands.remove(menu);
+    }
+
+    public void fillActions(Map<String, Action> all) {
+        all.putAll(commands);
+    }
+    
+    
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,112 @@
+package aQute.bnd.classpath;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jdt.core.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+import aQute.lib.osgi.*;
+
+public class BndContainer implements IClasspathContainer {
+    final static IClasspathEntry ICLASSPATHENTRY_EMPTY[] = new IClasspathEntry[0];
+
+    final Project                project;
+    volatile int                 count;
+    volatile IClasspathEntry[]   cachedEntries;
+
+    BndContainer(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Get the classpath entries ... seems to be called VERY often
+     */
+
+    public IClasspathEntry[] getClasspathEntries() {
+        boolean cached = cachedEntries != null && count == project.getChanged();
+        if (!cached) {
+            synchronized (project) {
+                count = project.getChanged();
+                try {
+                    ArrayList<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
+                    // fetch the names of all files that match our filter
+                    List<Container> entries = new ArrayList<Container>();
+                    entries.addAll(project.getBuildpath());
+
+                    // The first file is always the project directory, Eclipse
+                    // already includes that for us.
+                    if (entries.size() > 0)
+                        entries.remove(0);
+                    else
+                        System.err.println("Huh? Should have the bin dir! "
+                                + entries);
+                    // Eclipse does not know a boot classpath, but it compiles
+                    // against
+                    // a jre. We add anything on the bootpath
+                    entries.addAll(project.getBootclasspath());
+
+                    for (Container c : entries) {
+                        IClasspathEntry cpe;
+                        IPath sourceAttachment = null;
+                        
+                        if (c.getError() == null) {
+                            File file = c.getFile();
+                            assert file.isAbsolute();
+                            
+                            IPath p = Central.toPath(project, file);
+                            // JDT seems to ignore files when they
+                            //
+                            try {
+                                Central.refresh(p);
+                            } catch (Throwable e) {
+
+                            }
+                            if ( c.getType()==Container.TYPE.PROJECT ) {
+                                File sourceDir = c.getProject().getSrc();
+                                if (sourceDir.isDirectory() )
+                                    sourceAttachment = Central.toPath(c.getProject(), sourceDir );
+                            }
+                                
+                            cpe = JavaCore.newLibraryEntry(p, sourceAttachment, null);
+                            result.add(cpe);
+                        }
+                    }
+                    cachedEntries = result.toArray(ICLASSPATHENTRY_EMPTY);
+                } catch (Exception e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+        return cachedEntries;
+    }
+
+    private Collection<File> toFiles(Collection<Container> path) {
+        List<File> result = new ArrayList<File>(path.size());
+        for (Container c : path) {
+            if (c.getError() == null) {
+                result.add(c.getFile());
+            }
+        }
+        return result;
+    }
+
+    public String getDescription() {
+        return "bnd";
+    }
+
+    public int getKind() {
+        return IClasspathContainer.K_APPLICATION;
+    }
+
+    public IPath getPath() {
+        return BndContainerInitializer.ID;
+    }
+
+    public Project getModel() {
+        return project;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerInitializer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerInitializer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerInitializer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,97 @@
+package aQute.bnd.classpath;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jdt.core.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+
+/**
+ * A bnd container reads the bnd.bnd file in the project directory and use the
+ * information in there to establish the classpath. The classpath is defined by
+ * the -build-env instruction. This instruction contains a list of bsn's that
+ * are searched in the available repositories and returned as File objects.
+ * 
+ * This initializer establishes the link between the container object and the
+ * BndModel. The container object is just a delegator because for some unknown
+ * reasons, you can only update the container (refresh the contents) when you
+ * give it a new object ;-(
+ * 
+ * Because this plugin uses the Bnd Builder in different places, the Bnd Model
+ * is centralized and available from the Activator.
+ */
+public class BndContainerInitializer extends ClasspathContainerInitializer
+        implements ModelListener {
+
+    public final static Path ID      = new Path("aQute.bnd.classpath.container");
+
+    final Central            central = Activator.getDefault().getCentral();
+
+    public BndContainerInitializer() {
+        central.addModelListener(this);
+    }
+
+    /**
+     * Called when a new project is found. This class is instantiated once and
+     * then used for any project that has a bnd container associated. We create
+     * a link between the project and the Bnd Model. A delegating container
+     * object is created to link the project to the container so it can get its
+     * classpath entries. Note that the container object is not stored or
+     * remembered because we create a new one for every update (otherwise the
+     * update is not visible for some reason)
+     */
+    public void initialize(IPath containerPath, IJavaProject project)
+            throws CoreException {
+
+        // We maintain the models in the actitvator because other
+        // parts also use the model. Unfortunately, one can only
+        // do this with a static method :-(
+
+        Project model = central.getModel(project);
+        if (model == null)
+            throw new CoreException(
+                    new Status(IStatus.ERROR, ID.toString(),
+                            "Can not create model, likely the project does not contain a bnd.bnd file"));
+
+        // Update the Java Model so the changes become visible.
+        // Notice the unreferenced object.
+        requestClasspathContainerUpdate(containerPath, project,
+                new BndContainer(model));
+
+    }
+
+    /**
+     * We can always update.
+     */
+    public boolean canUpdateClasspathContainer(IPath containerPath,
+            IJavaProject project) {
+        return true;
+    }
+
+    /**
+     * Update the container. The containerSuggestion should always be a new
+     * BndContainer ...
+     */
+    public void requestClasspathContainerUpdate(IPath containerPath,
+            IJavaProject project, IClasspathContainer containerSuggestion)
+            throws CoreException {
+
+        JavaCore.setClasspathContainer(containerPath,
+                new IJavaProject[] { project },
+                new IClasspathContainer[] { containerSuggestion }, null);
+    }
+
+    public void modelChanged(Project model) throws Exception {
+        IJavaProject project = central.getJavaProject(model);
+        if (model == null || project == null) {
+            System.out.println("Help! No IJavaProject for " + model);
+        } else
+            requestClasspathContainerUpdate(ID, project,
+                    new BndContainer(model));
+    }
+
+    public void workspaceChanged(Workspace ws) throws Exception {
+        System.out.println("Workspace changed");
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerPage.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerPage.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/BndContainerPage.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,252 @@
+package aQute.bnd.classpath;
+
+import java.io.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.ui.wizards.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.wizard.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+
+public class BndContainerPage extends WizardPage implements
+        IClasspathContainerPage, IClasspathContainerPageExtension {
+
+    // private Activator activator = Activator.getActivator();
+
+    private Table        table;
+    private Project      model;
+    private File         basedir;
+    private IJavaProject javaProject;
+
+    /**
+     * Default Constructor - sets title, page name, description
+     */
+    public BndContainerPage() {
+        super("bnd", "bnd - classpath", null);
+        setDescription("Ensures that bnd sees the same classpath as eclipse. The table will show the current contents. If there is no bnd file, you can create it with the button");
+        setPageComplete(true);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jdt.ui.wizards.IClasspathContainerPageExtension#initialize(org.eclipse.jdt.core.IJavaProject,
+     *      org.eclipse.jdt.core.IClasspathEntry[])
+     */
+    public void initialize(IJavaProject project,
+            IClasspathEntry[] currentEntries) {
+        javaProject = project;
+        model = Activator.getDefault().getCentral().getModel(project);
+        basedir = project.getProject().getLocation().makeAbsolute().toFile();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+     */
+    public void createControl(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NULL);
+        composite.setLayout(new FormLayout());
+        composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
+                | GridData.HORIZONTAL_ALIGN_FILL));
+        composite.setFont(parent.getFont());
+
+        setControl(composite);
+
+        final Button wCreate = new Button(composite, SWT.NONE);
+        wCreate.setEnabled(model == null);
+        final FormData fd_wCreate = new FormData();
+        fd_wCreate.bottom = new FormAttachment(100, -5);
+        fd_wCreate.right = new FormAttachment(100, -4);
+        wCreate.setLayoutData(fd_wCreate);
+        wCreate.setText("Create bnd.bnd");
+
+        final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
+        table = tableViewer.getTable();
+        final FormData fd_table = new FormData();
+        fd_table.top = new FormAttachment(0, 3);
+        fd_table.left = new FormAttachment(0, 3);
+        fd_table.right = new FormAttachment(100, -4);
+        fd_table.bottom = new FormAttachment(100, -37);
+        table.setLayoutData(fd_table);
+        table.setLinesVisible(true);
+        table.setHeaderVisible(true);
+
+        final TableColumn wBsn = new TableColumn(table, SWT.NONE);
+        wBsn.setWidth(200);
+        wBsn.setText("Bundle Symbolic Name");
+
+        final TableColumn wVersion = new TableColumn(table, SWT.NONE);
+        wVersion.setWidth(100);
+        wVersion.setText("Version");
+
+        final TableColumn wOptions = new TableColumn(table, SWT.NONE);
+        wOptions.setWidth(200);
+        wOptions.setText("Options");
+
+        final TableColumn wFile = new TableColumn(table, SWT.NONE);
+        wFile.setWidth(100);
+        wFile.setText("File");
+
+        tableViewer.setContentProvider(new IStructuredContentProvider() {
+
+            public Object[] getElements(Object inputElement) {
+                if (model != null)
+                    try {
+                        
+                        return model.getBuildpath().toArray();
+                    } catch (Exception e) {
+                        // TODO Auto-generated catch block
+                        e.printStackTrace();
+                    }
+                return new Object[0];
+
+            }
+
+            public void dispose() {
+                // TODO Auto-generated method stub
+
+            }
+
+            public void inputChanged(Viewer viewer, Object oldInput,
+                    Object newInput) {
+
+            }
+
+        });
+        tableViewer.setLabelProvider(new ITableLabelProvider() {
+
+            public Image getColumnImage(Object element, int columnIndex) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            public String getColumnText(Object element, int columnIndex) {
+                Container c = (Container) element;
+                switch (columnIndex) {
+                case 0:
+                    return c.getBundleSymbolicName();
+                case 1:
+                    return c.getVersion();
+                case 2:
+                    return c.getError();
+                case 3:
+                    return c.getFile() + " (" + (c.getFile()!=null && c.getFile().exists() ? "exists" : "?") + ")";
+                }
+                return null;
+            }
+
+            public void addListener(ILabelProviderListener listener) {
+                // TODO Auto-generated method stub
+
+            }
+
+            public void dispose() {
+                // TODO Auto-generated method stub
+
+            }
+
+            public boolean isLabelProperty(Object element, String property) {
+                // TODO Auto-generated method stub
+                return false;
+            }
+
+            public void removeListener(ILabelProviderListener listener) {
+                // TODO Auto-generated method stub
+
+            }
+
+        });
+        tableViewer.setInput(model);
+        wCreate.addSelectionListener(new SelectionListener() {
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                System.out.println("defw selected");
+            }
+
+            public void widgetSelected(SelectionEvent e) {
+                wCreate.setEnabled(!createBnd());
+                tableViewer.setInput(model);
+            }
+
+        });
+    }
+
+    protected boolean createBnd() {
+        if (basedir != null && basedir.isDirectory()) {
+            File bnd = new File(basedir, "bnd.bnd");
+            try {
+                FileOutputStream out = new FileOutputStream(bnd);
+                PrintStream ps = new PrintStream(out);
+                try {
+                    ps.println("# Auto generated by bnd, please adapt");
+                    ps.println();
+                    ps.println("Export-Package:                    ");
+                    ps.println("Private-Package:                   ");
+                    ps
+                            .println("Bundle-Name:                       ${Bundle-SymbolicName}");
+                    ps.println("Bundle-Version:                    1.0");
+                    ps.println();
+                    ps.println("#Example buildpath");
+                    ps
+                            .println("-buildpath:                        osgi;                                        version=4.0, \\");
+                    ps
+                            .println("                                   com.springsource.junit;                      version=\"[3.8,4)\"");
+                } finally {
+                    out.close();
+                    ps.close();
+                }
+                javaProject.getResource().refreshLocal(IResource.DEPTH_ONE, null);
+                model = Activator.getDefault().getCentral().getModel(javaProject);
+                return model != null;
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (CoreException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jdt.ui.wizards.IClasspathContainerPage#finish()
+     */
+    public boolean finish() {
+        return true;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jdt.ui.wizards.IClasspathContainerPage#getSelection()
+     */
+    public IClasspathEntry getSelection() {
+        IPath containerPath = BndContainerInitializer.ID;
+        IClasspathEntry cpe = JavaCore.newContainerEntry(containerPath);
+        return cpe;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jdt.ui.wizards.IClasspathContainerPage#setSelection(org.eclipse.jdt.core.IClasspathEntry)
+     */
+    public void setSelection(IClasspathEntry containerEntry) {
+        if (containerEntry != null) {
+            // initPath = containerEntry.getPath();
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/ModelListener.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/ModelListener.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/classpath/ModelListener.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,8 @@
+package aQute.bnd.classpath;
+
+import aQute.bnd.build.*;
+
+
+public interface ModelListener {
+    void modelChanged(Project model) throws Exception;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/help/Syntax.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/help/Syntax.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/help/Syntax.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,482 @@
+package aQute.bnd.help;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.lib.osgi.*;
+
+public class Syntax implements Constants {
+    final String                            header;
+    final String                            lead;
+    final String                            example;
+    final Pattern                           pattern;
+    final String                            values;
+    final Syntax[]                          children;
+
+    static Syntax                           version              = new Syntax(
+                                                                         VERSION_ATTRIBUTE,
+                                                                         "A version range to select the version of an export definition. The default value is 0.0.0 .",
+                                                                         "version=\"[1.2,3.0)\"",
+                                                                         null,
+                                                                         Verifier.VERSIONRANGE);
+    static Syntax                           bundle_symbolic_name = new Syntax(
+                                                                         BUNDLE_SYMBOLIC_NAME_ATTRIBUTE,
+                                                                         "The bundle symbolic name of the exporting bundle.",
+                                                                         "bundle-symbolic-name=com.acme.foo.daffy",
+                                                                         null,
+                                                                         Verifier.SYMBOLICNAME);
+
+    static Syntax                           bundle_version       = new Syntax(
+                                                                         BUNDLE_VERSION_ATTRIBUTE,
+                                                                         "a version range to select the bundle version of the exporting bundle. The default value is 0.0.0.",
+                                                                         "bundle-version=1.3",
+                                                                         null,
+                                                                         Verifier.VERSIONRANGE);
+
+    static Syntax                           path_version         = new Syntax(
+                                                                         VERSION_ATTRIBUTE,
+                                                                         "Specifies the range in the repository, project, or file",
+                                                                         "version=project",
+                                                                         "project,type",
+                                                                         Pattern
+                                                                                 .compile("project|type|"
+                                                                                         + Verifier.VERSIONRANGE
+                                                                                                 .toString()));
+
+    static Syntax[]                         syntaxes             = new Syntax[] {
+            new Syntax(
+                    BUNDLE_ACTIVATIONPOLICY,
+                    "The Bundle-ActivationPolicy specifies how the framework should activate the bundle once started. ",
+                    "Bundle-ActivationPolicy: lazy", "lazy", Pattern
+                            .compile("lazy")),
+
+            new Syntax(
+                    BUNDLE_ACTIVATOR,
+                    "The Bundle-Activator header specifies the name of the class used to start and stop the bundle. ",
+                    "Bundle-Activator: com.acme.foo.Activator",
+                    "${classes;implementing;org.osgi.framework.BundleActivator}",
+                    Verifier.FQNPATTERN),
+            new Syntax(
+                    BUNDLE_CATEGORY,
+                    "The Bundle-Category header holds a comma-separated list of category names",
+                    "Bundle-Category: test",
+                    "osgi,test,game,util,eclipse,netbeans,jdk,specification",
+                    null),
+            new Syntax(
+                    BUNDLE_CLASSPATH,
+                    "The Bundle-ClassPath header defines a comma-separated list of JAR file path names or directories (inside the bundle) containing classes and resources. The period (’.’) specifies the root directory of the bundle’s JAR. The period is also the default.",
+                    "Bundle-Classpath: /lib/libnewgen.so, .", null,
+                    Verifier.PATHPATTERN),
+            new Syntax(
+                    BUNDLE_CONTACTADDRESS,
+                    "The Bundle-ContactAddress header provides the contact address of the vendor. ",
+                    "Bundle-ContactAddress: 2400 Oswego Road, Austin, TX 74563",
+                    null, null),
+            new Syntax(
+                    BUNDLE_COPYRIGHT,
+                    "The Bundle-Copyright header contains the copyright specification for this bundle. ",
+                    "Bundle-Copyright: OSGi (c) 2002", null, null),
+            new Syntax(
+                    BUNDLE_DESCRIPTION,
+                    "The Bundle-Description header defines a short description of this bundle.",
+                    "Bundle-Description: Ceci ce n'est pas une bundle", null,
+                    null),
+
+            new Syntax(
+                    BUNDLE_DOCURL,
+                    "The Bundle-DocURL headers must contain a URL pointing to documentation about this bundle.",
+                    "Bundle-DocURL: http://www.aQute.biz/Code/Bnd", null,
+                    Verifier.URLPATTERN),
+
+            new Syntax(
+                    BUNDLE_ICON,
+                    "The optional Bundle-Icon header provides a list of (relative) URLs to icons representing this bundle in different sizes. ",
+                    "Bundle-Icon: /icons/bnd.png;size=64", "/icons/bundle.png",
+                    Verifier.URLPATTERN, new Syntax("size",
+                            "Icons size in pixels, e.g. 64", "64",
+                            "16,32,48,64,128", Verifier.NUMBERPATTERN)),
+
+            new Syntax(
+                    BUNDLE_LICENSE,
+                    "The Bundle-License header provides an optional machine readable form of license information. The purpose of this header is to automate some of the license processing required by many organizations",
+                    "Bundle License: http://www.opensource.org/licenses/jabberpl.php",
+                    "http://www.apache.org/licenses/LICENSE-2.0,<<EXTERNAL>>",
+                    Pattern.compile("(" + Verifier.URLPATTERN
+                            + "|<<EXTERNAL>>)"), new Syntax(
+                            DESCRIPTION_ATTRIBUTE,
+                            "Human readable description of the license",
+                            "description=\"Described the license here\"", null,
+                            Verifier.ANYPATTERN), new Syntax(LINK_ATTRIBUTE,
+                            "", "", null, Verifier.URLPATTERN)),
+            new Syntax(
+                    BUNDLE_LOCALIZATION,
+                    "The Bundle-Localization header contains the location in the bundle where localization files can be found. The default value is OSGI-INF/l10n/bundle. Translations are by default therefore OSGI-INF/l10n/bundle_de.properties, OSGI-INF/l10n/bundle_nl.properties, etc.",
+                    "Bundle-Localization: OSGI-INF/l10n/bundle",
+                    "OSGI-INF/l10n/bundle", Verifier.URLPATTERN),
+            new Syntax(
+                    BUNDLE_MANIFESTVERSION,
+                    "This header is set by bnd automatically to 2. The Bundle-ManifestVersion header defines that the bundle follows the rules of this specification. The Bundle-ManifestVersion header determines whether the bundle follows the rules of this specification.",
+                    "# Bundle-ManifestVersion: 2", "2", Verifier.NUMBERPATTERN),
+            new Syntax(
+                    BUNDLE_NAME,
+                    "This header will be derived from the  Bundle-SymbolicName if not set. The Bundle-Name header defines a readable name for this bundle. This should be a short, human-readable name that can contain spaces.",
+                    "Bundle-Name: My Bundle", null, Verifier.ANYPATTERN),
+            new Syntax(
+                    BUNDLE_NATIVECODE,
+                    "The Bundle-NativeCode header contains a specification of native code libraries contained in this bundle. ",
+                    "Bundle-NativeCode: /lib/http.DLL; osname = QNX; osversion = 3.1",
+                    null,
+                    Verifier.PATHPATTERN,
+                    new Syntax(OSNAME_ATTRIBUTE,
+                            "The name of the operating system", "osname=MacOS",
+                            Processor.join(Verifier.OSNAMES, ","),
+                            Verifier.ANYPATTERN),
+                    new Syntax(OSVERSION_ATTRIBUTE, "Operating System Version",
+                            "osversion=3.1", null, Verifier.ANYPATTERN),
+                    new Syntax(LANGUAGE_ATTRIBUTE, "Language ISO 639 code",
+                            "language=nl", null, Verifier.ISO639),
+                    new Syntax(PROCESSOR_ATTRIBUTE, "Processor name",
+                            "processor=x86", Processor.join(
+                                    Verifier.PROCESSORNAMES, ","),
+                            Verifier.ANYPATTERN),
+                    new Syntax(
+                            SELECTION_FILTER_ATTRIBUTE,
+                            "The value of this attribute must be a filter expression that indicates if the native code clause should be selected or not.",
+                            "selection-filter=\"(com.acme.windowing=win32)\"",
+                            null, Verifier.FILTERPATTERN)),
+            new Syntax(
+                    BUNDLE_REQUIREDEXECUTIONENVIRONMENT,
+                    "The Bundle-RequiredExecutionEnvironment contains a comma-separated list of execution environments that must be present on the Service Platform.",
+                    "Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0",
+                    Processor.join(Verifier.EES, ","), Verifier.ANYPATTERN),
+
+            new Syntax(
+                    BUNDLE_SYMBOLICNAME,
+                    "The Bundle-SymbolicName header specifies a non-localizable name for this bundle. The bundle symbolic name together with a version must identify a  unique bundle. The bundle symbolic name should be based on the reverse  domain name convention",
+                    "Bundle-SymbolicName: com.acme.foo.daffy;singleton:=true",
+                    "${p}",
+                    Verifier.SYMBOLICNAME,
+                    new Syntax(
+                            SINGLETON_DIRECTIVE,
+                            " Indicates that the bundle can only have  a single version resolved.  A value of true indicates that the bundle is a singleton bundle. The default value is false. The Framework must resolve at most one  bundle when multiple versions of a singleton bundle with the same symbolic name are installed. Singleton bundles do not affect the resolution of non-singleton bundles with the same symbolic name.",
+                            "false", "true,false", Verifier.TRUEORFALSEPATTERN),
+                    new Syntax(
+                            FRAGMENT_ATTACHMENT_DIRECTIVE,
+                            "Defines how fragments are allowed to be attached, see the fragments in Fragment Bundles on page73. The following values are valid for this directive:",
+                            "", "always|never|resolve-time", Pattern
+                                    .compile("always|never|resolve-time")),
+                    new Syntax(BLUEPRINT_WAIT_FOR_DEPENDENCIES_ATTRIBUTE, "",
+                            "", "true,false", Verifier.TRUEORFALSEPATTERN),
+                    new Syntax(BLUEPRINT_TIMEOUT_ATTRIBUTE, "", "",
+                            "30000,60000,300000", Verifier.NUMBERPATTERN)),
+
+            new Syntax(
+                    BUNDLE_UPDATELOCATION,
+                    "The Bundle-UpdateLocation header specifies a URL where an update for this bundle should come from. If the bundle is updated, this location should be used, if present, to retrieve the updated JAR file.",
+                    "Bundle-UpdateLocation: http://www.acme.com/Firewall/bundle.jar",
+                    null, Verifier.URLPATTERN),
+
+            new Syntax(
+                    BUNDLE_VENDOR,
+                    "The Bundle-Vendor header contains a human-readable description of the bundle vendor. ",
+                    "Bundle-Vendor: OSGi Alliance ", null, null),
+
+            new Syntax(
+                    BUNDLE_VERSION,
+                    "The Bundle-Version header specifies the version of this bundle",
+                    "Bundle-Version: 1.23.4.build200903221000", null,
+                    Verifier.VERSION),
+
+            new Syntax(
+                    DYNAMICIMPORT_PACKAGE,
+                    "The DynamicImport-Package header contains a comma-separated list of package names that should be dynamically imported when needed.",
+                    "DynamicImport-Package: com.acme.plugin.*", "",
+                    Verifier.WILDCARDNAMEPATTERN, version,
+                    bundle_symbolic_name, bundle_version),
+
+            new Syntax(
+                    EXPORT_PACKAGE,
+                    "The Export-Package header contains a declaration of exported packages.",
+                    "Export-Package: org.osgi.util.tracker;version=1.3",
+                    "${packages}",
+                    null,
+                    new Syntax(
+                            NO_IMPORT_DIRECTIVE,
+                            "By default, bnd makes all exports also imports. Adding a -noimport to an exported package will make it export only",
+                            "-noimport:=true", "true,false",
+                            Verifier.TRUEORFALSEPATTERN),
+                    new Syntax(
+                            USES_DIRECTIVE,
+                            "Calculated by bnd: It is a comma-separated list of package names that are used by the exported package",
+                            "Is calculated by bnd", null, null),
+                    new Syntax(
+                            MANDATORY_DIRECTIVE,
+                            "A comma-separated list of attribute names. Note that the use of a comma in the value requires it to be enclosed in double quotes. A bundle importing the package must specify the mandatory attributes, with a value that matches, to resolve to the exported package",
+                            "mandatory=\"bar,foo\"", null, null),
+                    new Syntax(
+                            INCLUDE_DIRECTIVE,
+                            "A comma-separated list of class names that must be visible to an importer",
+                            "include:=\"Qux*\"", null, null),
+                    new Syntax(
+                            EXCLUDE_DIRECTIVE,
+                            "A comma-separated list of class names that must not be visible to an importer",
+                            "exclude:=\"QuxImpl*,BarImpl\"", null,
+                            Verifier.WILDCARDNAMEPATTERN), new Syntax(
+                            IMPORT_DIRECTIVE, "Experimental", "", null, null)
+
+            ),
+            new Syntax(EXPORT_SERVICE, "Deprecated",
+                    "Export-Service: org.osgi.service.log.LogService ",
+                    "${classes;implementing;*}", null),
+            new Syntax(
+                    FRAGMENT_HOST,
+                    "The Fragment-Host header defines the host bundle for this fragment.",
+                    "Fragment-Host: org.eclipse.swt; bundle-version=\"[3.0.0,4.0.0)\"",
+                    null,
+                    null,
+                    new Syntax(
+                            EXTENSION_DIRECTIVE,
+                            " Indicates this extension is a system or boot class path extension. It is only applicable when the Fragment-Host is the System Bundle",
+                            "extension:=framework", "framework,bootclasspath",
+                            Pattern.compile("framework|bootclasspath")),
+                    bundle_version),
+            new Syntax(
+                    IMPORT_PACKAGE,
+                    "This header is normally calculated by bnd, however, you can decorate packages or skip packages. The Import-Package header declares the imported packages for this bundle",
+                    "Import-Package: !com.exotic.*, com.acme.foo;vendor=ACME, *",
+                    "${exported_packages}",
+                    Verifier.WILDCARDNAMEPATTERN,
+                    new Syntax(
+                            REMOVE_ATTRIBUTE_DIRECTIVE,
+                            "Remove the given attributes from matching imported packages",
+                            "-remove-attribute:=foo.*", null,
+                            Verifier.WILDCARDNAMEPATTERN),
+                    new Syntax(
+                            RESOLUTION_DIRECTIVE,
+                            "Indicates that the packages must be resolved if the value is mandatory, which is the default. If mandatory packages cannot be resolved, then the bundle must fail to resolve. A value of optional indicates that the packages are optional",
+                            "resolution:=optional", "mandatory,optional",
+                            Pattern.compile("mandatory|optional")
+
+                    ), version, bundle_symbolic_name, bundle_version),
+
+            new Syntax(
+                    REQUIRE_BUNDLE,
+                    "The Require-Bundle header specifies the required exports from another bundle.",
+                    "Require-Bundle: com.acme.chess",
+                    null,
+                    Verifier.WILDCARDNAMEPATTERN,
+
+                    new Syntax(
+                            VISIBILITY_DIRECTIVE,
+                            " If the value is private (Default), then all visible packages from the required bundles are not re-exported. If the value is reexport then bundles that require this bundle will transitively have access to these required bundle’s exported packages.",
+                            "visibility:=private", "private,reexport", Pattern
+                                    .compile("private|reexport")),
+
+                    new Syntax(
+                            RESOLUTION_DIRECTIVE,
+                            "If the value is mandatory (default) then the required bundle must exist for this bundle to resolve. If the value is optional, the bundle will resolve even if the required bundle does not exist.",
+                            "resolution:=optional", "mandatory,optional",
+                            Pattern.compile("mandatory|optional")),
+
+                    new Syntax(
+                            SPLIT_PACKAGE_DIRECTIVE,
+                            "Indicates how an imported package should be merged when it is split between different exporters. The default is merge-first with warning",
+                            "-split-package:=merge-first",
+                            "merge-first,merge-last,error,first",
+                            Pattern
+                                    .compile("merge-first|merge-last|error|first")),
+                    bundle_version
+
+            ),
+
+            new Syntax(
+                    BUILDPATH,
+                    "Provides the class path for building the jar. The entries are references to the repository",
+                    "-buildpath=osgi;version=4.1", "${repo;bsns}",
+                    Verifier.SYMBOLICNAME, path_version),
+            new Syntax(
+                    BUMPPOLICY,
+                    "Sets the version bump policy. This is a parameter to the ${version} macro.",
+                    "-bumppolicy==+0", "==+,=+0,+00", Pattern
+                            .compile("[=+-0][=+-0][=+-0]")),
+
+            new Syntax(
+                    CONDUIT,
+                    "Allows a bnd file to point to files which will be returned when the bnd file is build",
+                    "-conduit= jar/osgi.jar", null, null),
+
+            new Syntax(
+                    DEPENDSON,
+                    "List of project names that this project directly depends on. These projects are always build ahead of this project",
+                    "-dependson=org.acme.cm", "${projects}", null),
+
+            new Syntax(DEPLOYREPO,
+                    "Specifies to which repo the project should be deployed.",
+                    "-deployrepo=cnf", "${repos}", null),
+
+            new Syntax(
+                    DONOTCOPY,
+                    "Regular expression for names of files and directories that should not be copied when discovered",
+                    "-donotcopy=(CVS|\\.svn)", null, null),
+
+            new Syntax(
+                    EXPORT_CONTENTS,
+                    "Build the JAR in the normal way but use this header for the Export-Package header manifest generation, same format",
+                    "-exportcontents=!*impl*,*;version=3.0", null, null),
+
+            new Syntax(
+                    FAIL_OK,
+                    "Return with an ok status (0) even if the build generates errors",
+                    "-failok=true", "true,false", Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(
+                    INCLUDE,
+                    "Include files. If an entry starts with '-', it does not have to exist. If it starts with '~', it must not overwrite any existing properties",
+                    "-include: -${java.user}/.bnd", null, null),
+
+            new Syntax(
+                    INCLUDERESOURCE,
+                    "Include resources from the file system. You can specify a directory, or file. All files are copied to the root, unless a destination directory is indicated",
+                    "-includeresource: lib=jar", null, null),
+
+            new Syntax(
+                    MAKE,
+                    "Set patterns for make plugins. These patterns are used to find a plugin that can make a resource that can not be found.",
+                    "-make: (*).jar;type=bnd;  recipe=\"bnd/$1.bnd\"", null,
+                    null, new Syntax("type", "Type name for plugin",
+                            "type=bnd", "bnd", null), new Syntax("recipe",
+                            "Recipe for the plugin, can use back references",
+                            "recipe=\"bnd/$1.bnd\"", "bnd", null)),
+
+            new Syntax(
+                    MANIFEST,
+                    "Directly include a manifest, do not use the calculated manifest",
+                    "-manifest = META-INF/MANIFEST.MF", null, null),
+
+            new Syntax(NOEXTRAHEADERS, "Do not generate housekeeping headers",
+                    "-noextraheaders", "true,false",
+                    Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(NOUSES,
+                    "Do not calculate the uses: directive on exports",
+                    "-nouses=true", "true,false", Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(NOPE,
+                    "Dont do anything, return without building any jars",
+                    "-nope=true", "true,false", Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(
+                    PEDANTIC,
+                    "Warn about things that are not really wrong but still not right",
+                    "-nope=true", "true,false", Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(
+                    PLUGIN,
+                    "Define the plugins",
+                    "-plugin=aQute.lib.spring.SpringComponent,aQute.lib.deployer.FileRepo;location=${repo}",
+                    null, null),
+
+            new Syntax(
+                    SERVICE_COMPONENT,
+                    "The header for Declarative Services",
+                    "Service-Component=com.acme.Foo?;activate='start'",
+                    null, null),
+
+            new Syntax(POM, "Generate a maven pom", "-pom=true", "true,false",
+                    Verifier.TRUEORFALSEPATTERN),
+
+            new Syntax(RELEASEREPO,
+                    "Specifies to which repo the project should be released.",
+                    "-releaserepo=cnf", "${repos}", null),
+
+            new Syntax(REMOVE_HEADERS,
+                    "Remove all headers that match the regular expressions",
+                    "-removeheaders=FOO_.*,Proprietary", null, null),
+            new Syntax(
+                    RESOURCEONLY,
+                    "Normally bnd warns when the JAR does not contain any classes, this option suppresses this warning",
+                    "-resourceonly=true", "true,false",
+                    Verifier.TRUEORFALSEPATTERN),
+            new Syntax(SOURCES, "Include sources in the jar", "-sources=true",
+                    "true,false", Verifier.TRUEORFALSEPATTERN),
+            new Syntax(
+                    SOURCEPATH,
+                    "List of directory names that used to source sources for -sources",
+                    "-sourcepath:= src, test", null, null),
+            new Syntax(
+                    SUB,
+                    "Build a set of bnd files that use this bnd file as a basis. The list of bnd file can be specified with wildcards",
+                    "-sub=com.acme.*.bnd", null, null),
+            new Syntax(
+                    RUNPROPERTIES,
+                    "Properties that are set as system properties before the framework is started",
+                    "-runproperties= foo=3, bar=4", null, null),
+            new Syntax(RUNSYSTEMPACKAGES,
+                    "Add additional system packages to a framework run",
+                    "-runsystempackages=com.acme.foo,javax.management", null,
+                    null),
+            new Syntax(
+                    RUNBUNDLES,
+                    "Add additional bundles, specified with their bsn and version like in -buildpath, that are started before the project is run",
+                    "-runbundles=osgi;version=\"[4.1,4.2)\", junit.junit, com.acme.foo;version=project",
+                    null, Verifier.SYMBOLICNAME, path_version),
+            new Syntax(
+                    RUNPATH,
+                    "Additional JARs for the VM path, should include the framework",
+                    "-runpath=org.eclipse.osgi;version=3.5", null, null,
+                    path_version),
+            new Syntax(
+                    RUNVM,
+                    "Additional arguments for the VM invokation. Keys that start with a - are added as options, otherwise they are treated as -D properties for the VM",
+                    "-runvm=-Xmax=30", null, null),
+            new Syntax(
+                    VERSIONPOLICY,
+                    "Provides a version policy to imports that are calculated from exports",
+                    "-versionpolicy = \"[${version;==;${@}},${version;+;${@}})\"",
+                    null, null)
+
+                                                                 };
+
+    public final static Map<String, Syntax> HELP                 = new HashMap<String, Syntax>();
+
+    static {
+        for (Syntax s : syntaxes) {
+            HELP.put(s.header, s);
+        }
+    }
+
+    public Syntax(String header, String lead, String example, String values,
+            Pattern pattern, Syntax... children) {
+        this.header = header;
+        this.children = children;
+        this.lead = lead;
+        this.example = example;
+        this.values = values;
+        this.pattern = pattern;
+    }
+
+    public String getLead() {
+        return lead;
+    }
+
+    public String getExample() {
+        return example;
+    }
+
+    public String getValues() {
+        return values;
+    }
+
+    public String getPattern() {
+        return lead;
+    }
+
+    public Syntax[] getChildren() {
+        return children;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarConfiguration.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarConfiguration.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarConfiguration.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.jareditor;
+
+import org.eclipse.jface.text.source.*;
+
+public class JarConfiguration extends SourceViewerConfiguration {
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarDocumentProvider.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarDocumentProvider.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarDocumentProvider.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,108 @@
+package aQute.bnd.jareditor;
+
+import java.io.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.source.*;
+import org.eclipse.ui.part.*;
+import org.eclipse.ui.texteditor.*;
+
+public class JarDocumentProvider implements IDocumentProvider {
+	Document	doc;
+	PrintWriter out;
+	
+	public void aboutToChange(Object arg0) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void addElementStateListener(IElementStateListener arg0) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public boolean canSaveDocument(Object arg0) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void changed(Object arg0) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void connect(Object source) throws CoreException {
+		try {
+			if (source instanceof FileEditorInput) {
+				FileEditorInput input = (FileEditorInput) source;
+				File file = input.getPath().toFile();
+				doc = new Document(print(file));
+			}
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public void disconnect(Object arg0) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public IAnnotationModel getAnnotationModel(Object arg0) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IDocument getDocument(Object arg0) {
+		return doc;
+	}
+
+	public long getModificationStamp(Object arg0) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public long getSynchronizationStamp(Object arg0) {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public boolean isDeleted(Object arg0) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean mustSaveDocument(Object arg0) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void removeElementStateListener(IElementStateListener arg0) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void resetDocument(Object arg0) throws CoreException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void saveDocument(IProgressMonitor arg0, Object arg1,
+			IDocument arg2, boolean arg3) throws CoreException {
+		// TODO Auto-generated method stub
+
+	}
+	
+	
+	String print(File file) throws Exception {
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+		PrintStream ps = new PrintStream(bos);
+		aQute.bnd.main.bnd x = new aQute.bnd.main.bnd();
+		x.setOut(ps);
+		x.doPrint(file.getAbsolutePath(), -1);
+		ps.close();
+		return new String( bos.toByteArray());
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarEditor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarEditor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/jareditor/JarEditor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,17 @@
+package aQute.bnd.jareditor;
+
+import org.eclipse.ui.texteditor.*;
+
+public class JarEditor extends AbstractTextEditor {
+	public JarEditor() {
+	      //install the source configuration
+	      setSourceViewerConfiguration(new JarConfiguration());
+	      //install the document provider
+	      setDocumentProvider(new JarDocumentProvider());
+	   }
+	   protected void createActions() {
+	      super.createActions();
+	      //... add other editor actions here
+	   }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiArgumentsTab.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiArgumentsTab.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiArgumentsTab.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,129 @@
+package aQute.bnd.junit;
+
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.*;
+import org.eclipse.debug.ui.*;
+import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.launching.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.widgets.*;
+
+import aQute.bnd.plugin.*;
+
+public class OSGiArgumentsTab extends AbstractLaunchConfigurationTab {
+    public static final String ATTR_KEEP = Activator.PLUGIN_ID
+                                                        + ".CLEARCACHE"; //$NON-NLS-1$
+    public static final String ATTR_REPORT   = Activator.PLUGIN_ID
+                                                        + ".REPORT"; //$NON-NLS-1$
+
+    Button                     wReport;
+    private Button             wKeep;
+
+    public void createControl(Composite parent) {
+        Composite comp = new Composite(parent, SWT.NONE);
+        comp.setLayout(new FormLayout());
+        setControl(comp);
+
+        wKeep = new Button(comp, SWT.CHECK);
+        final FormData fd_wClearCache = new FormData();
+        fd_wClearCache.bottom = new FormAttachment(wKeep, 25, SWT.BOTTOM);
+        fd_wClearCache.top = new FormAttachment(wKeep, 5, SWT.BOTTOM);
+        fd_wClearCache.right = new FormAttachment(wKeep, 189, SWT.LEFT);
+        fd_wClearCache.left = new FormAttachment(wKeep, 0, SWT.LEFT);
+        wKeep.setLayoutData(fd_wClearCache);
+        wKeep.setText("Keep");
+
+        wReport = new Button(comp, SWT.CHECK);
+        final FormData fd_verboseButton = new FormData();
+        fd_verboseButton.top = new FormAttachment(wReport, 5, SWT.BOTTOM);
+        fd_verboseButton.left = new FormAttachment(wReport, 0, SWT.LEFT);
+        wReport.setLayoutData(fd_verboseButton);
+        wReport.setText("Report");
+
+        validatePage();
+
+    }
+
+    private void validatePage() {
+        setErrorMessage(null);
+        setMessage(null);
+    }
+
+    public String getName() {
+        return "OSGi";
+    }
+
+    public void initializeFrom(ILaunchConfiguration configuration) {
+        try {
+            boolean keep = configuration.getAttribute(ATTR_KEEP,
+                    false);
+            wKeep.setSelection(keep);
+            boolean report = configuration.getAttribute(ATTR_REPORT,
+                    false);
+            wReport.setSelection(report);
+        } catch (Exception ce) {
+            ce.printStackTrace();
+        }
+    }
+
+    public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+        configuration.setAttribute(ATTR_REPORT, wReport.getSelection());
+        configuration
+                .setAttribute(ATTR_KEEP, wKeep.getSelection());
+    }
+
+    public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * Returns the Java project specified by the given launch configuration, or
+     * <code>null</code> if none.
+     * 
+     * @param configuration
+     *            launch configuration
+     * @return the Java project specified by the given launch configuration, or
+     *         <code>null</code> if none
+     * @exception CoreException
+     *                if unable to retrieve the attribute
+     */
+    public IJavaProject getJavaProject(ILaunchConfiguration configuration)
+            throws CoreException {
+        String projectName = getJavaProjectName(configuration);
+        if (projectName != null) {
+            projectName = projectName.trim();
+            if (projectName.length() > 0) {
+                IProject project = ResourcesPlugin.getWorkspace().getRoot()
+                        .getProject(projectName);
+                IJavaProject javaProject = JavaCore.create(project);
+                if (javaProject != null && javaProject.exists()) {
+                    return javaProject;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the Java project name specified by the given launch
+     * configuration, or <code>null</code> if none.
+     * 
+     * @param configuration
+     *            launch configuration
+     * @return the Java project name specified by the given launch
+     *         configuration, or <code>null</code> if none
+     * @exception CoreException
+     *                if unable to retrieve the attribute
+     */
+    public String getJavaProjectName(ILaunchConfiguration configuration)
+            throws CoreException {
+        return configuration.getAttribute(
+                IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
+                (String) null);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLaunchShortcut.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLaunchShortcut.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLaunchShortcut.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,15 @@
+package aQute.bnd.junit;
+
+
+
+public class OSGiJUnitLaunchShortcut extends
+		org.eclipse.jdt.junit.launcher.JUnitLaunchShortcut {
+	public OSGiJUnitLaunchShortcut() {
+		System.out.println("Constructore Launch Shortcut");
+	}
+	
+	protected String getLaunchConfigurationTypeId() {
+		return "aQute.bnd.junit.launchconfig";
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitLauncherConfigurationDelegate.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,107 @@
+package aQute.bnd.junit;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.*;
+import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.junit.launcher.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+import aQute.bnd.test.*;
+import aQute.lib.osgi.*;
+
+ at SuppressWarnings("unchecked")
+public class OSGiJUnitLauncherConfigurationDelegate extends
+        JUnitLaunchConfigurationDelegate {
+
+    public String verifyMainTypeName(ILaunchConfiguration configuration)
+            throws CoreException {
+        return "aQute.junit.runtime.Target";
+    }
+
+    // protected IMember[] evaluateTests(ILaunchConfiguration configuration,
+    // IProgressMonitor monitor) throws CoreException {
+    // System.out.println("Evaluate Tests");
+    // return super.evaluateTests(configuration, monitor);
+    // }
+
+    @SuppressWarnings("unchecked")
+    protected void collectExecutionArguments(
+            ILaunchConfiguration configuration, List/* String */vmArguments,
+            List/* String */programArguments) throws CoreException {
+
+        IJavaProject javaProject = getJavaProject(configuration);
+        Project model = Activator.getDefault().getCentral().getModel(javaProject);
+        model.clear();
+
+        try {
+            super.collectExecutionArguments(configuration, vmArguments,
+                    programArguments);
+
+            ProjectLauncher launcher = new ProjectLauncher(model);
+            launcher.getArguments(vmArguments, programArguments, true);
+
+            if (configuration.getAttribute(OSGiArgumentsTab.ATTR_KEEP, false))
+                programArguments.add("-keep");
+
+            if (launcher.isOk())
+                return;
+
+            String args = vmArguments + " " + programArguments + " "
+                    + Arrays.toString(getClasspath(configuration));
+            Activator.getDefault().report(true, false, launcher,
+                    "Launching " + model, args);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new CoreException(
+                    new Status(IStatus.ERROR, "osgi.eclipse.junit",
+                            "Building arguments for remote VM", e));
+        }
+        throw new CoreException(new Status(IStatus.ERROR, "osgi.eclipse.junit",
+                "Building arguments for remote VM, project=" + model, null));
+    }
+
+    /**
+     * Calculate the classpath. We include our own runtime.jar which includes
+     * the test framework and we include the first of the test frameworks
+     * specified.
+     */
+    public String[] getClasspath(ILaunchConfiguration configuration)
+            throws CoreException {
+        try {
+            IJavaProject javaProject = getJavaProject(configuration);
+            Project model = Activator.getDefault().getCentral().getModel(javaProject);
+
+            List<String> classpath = new ArrayList<String>();
+
+            classpath.add(getRuntime().getAbsolutePath());
+
+            for (Container c : model.getRunpath()) {
+                if (c.getType() != Container.TYPE.ERROR) {
+                    classpath.add(c.getFile().getAbsolutePath());
+                } else {
+                    abort("Invalid entry on the " + Constants.RUNPATH + ": "
+                            + c, null, IStatus.ERROR);
+                }
+            }
+            return classpath.toArray(new String[classpath.size()]);
+        } catch (Exception e) {
+            abort("Calculating class path", e, IStatus.ERROR);
+        }
+        return null;
+    }
+
+    /**
+     * Extract the runtime on the file system so we can refer to it. in the
+     * remote VM.
+     * 
+     * @return
+     */
+    public File getRuntime() {
+        return ProjectLauncher.getRuntime();
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitTabGroup.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitTabGroup.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/junit/OSGiJUnitTabGroup.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,26 @@
+package aQute.bnd.junit;
+
+
+import org.eclipse.debug.ui.*;
+import org.eclipse.debug.ui.sourcelookup.*;
+import org.eclipse.jdt.debug.ui.launchConfigurations.*;
+import org.eclipse.jdt.junit.launcher.*;
+
+
+ at SuppressWarnings("restriction")
+public class OSGiJUnitTabGroup extends org.eclipse.jdt.internal.junit.launcher.JUnitTabGroup {
+    public void createTabs(ILaunchConfigurationDialog dialog, String mode) {        
+        ILaunchConfigurationTab[] tabs= new ILaunchConfigurationTab[] {
+            new JUnitLaunchConfigurationTab(),
+            new OSGiArgumentsTab(),
+            new JavaArgumentsTab(),
+            new JavaClasspathTab(),
+            new JavaJRETab(),
+            new SourceLookupTab(),
+            new EnvironmentTab(),
+            new CommonTab()
+        };
+        setTabs(tabs);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchDelegate.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchDelegate.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchDelegate.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,55 @@
+package aQute.bnd.launch;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.debug.core.*;
+import org.eclipse.jdt.core.*;
+import org.eclipse.jdt.launching.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+import aQute.bnd.test.*;
+import aQute.lib.osgi.*;
+
+public class LaunchDelegate extends JavaLaunchDelegate {
+    String   programArguments;
+    String   vmArguments;
+    String[] classpath;
+
+    public void launch(ILaunchConfiguration configuration, String mode,
+            ILaunch launch, IProgressMonitor monitor) throws CoreException {
+        IJavaProject javaProject = getJavaProject(configuration);
+        Project model = Activator.getDefault().getCentral().getModel(javaProject);
+        try {
+            ProjectLauncher launcher = new ProjectLauncher(model);
+            List<String> vmArguments = new ArrayList<String>();
+            List<String> programArguments = new ArrayList<String>();
+            launcher.getArguments(vmArguments, programArguments, false);
+            this.classpath = launcher.getClasspath();
+            this.vmArguments = Processor.join(vmArguments, " ");
+            this.programArguments = Processor.join(programArguments, " ");
+            super.launch(configuration, mode, launch, monitor);
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    public String verifyMainTypeName(ILaunchConfiguration configuration)
+            throws CoreException {
+        return "aQute.junit.runtime.Target";
+    }
+
+    public String getVMArguments(ILaunchConfiguration c) {
+        return vmArguments;
+    }
+
+    public String getProgramArguments(ILaunchConfiguration c) {
+        return programArguments;
+    }
+
+    public String[] getClasspath(ILaunchConfiguration configuration) {
+        return classpath;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchTabGroup.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchTabGroup.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/LaunchTabGroup.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,21 @@
+package aQute.bnd.launch;
+
+import org.eclipse.debug.ui.*;
+import org.eclipse.debug.ui.sourcelookup.*;
+import org.eclipse.jdt.debug.ui.launchConfigurations.*;
+
+public class LaunchTabGroup extends AbstractLaunchConfigurationTabGroup {
+
+    public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+        ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
+                new JavaArgumentsTab(),
+                new JavaClasspathTab(),
+                new JavaJRETab(),
+                new SourceLookupTab(),
+                new EnvironmentTab(),
+                new CommonTab(),
+        };
+        setTabs(tabs);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/Shortcut.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/Shortcut.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/launch/Shortcut.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,120 @@
+package aQute.bnd.launch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.sourcelookup.*;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+
+public class Shortcut implements ILaunchShortcut {
+
+    public Shortcut() {
+        System.out.println("Constructor Launch Shortcut");
+    }
+
+    protected String getLaunchConfigurationTypeId() {
+        return "aQute.bnd.launch";
+    }
+
+    public void launch(ISelection selection, String mode) {
+        IStructuredSelection is = (IStructuredSelection) selection;
+        if ( is.getFirstElement() != null ) {
+            Object selected = is.getFirstElement();
+            IJavaElement element = null;
+            if (selected instanceof IJavaElement )
+                element = (IJavaElement) selected;
+            else if ( selected instanceof IAdaptable ) {
+                element = (IJavaElement) ((IAdaptable)selected).getAdapter(IJavaElement.class);
+            }
+            
+            if ( element != null )
+                launch(element.getJavaProject(), mode);
+        }
+        // TODO figure out which project we are in???
+    }
+
+    public void launch(IEditorPart editor, String mode) {
+        IEditorInput input = editor.getEditorInput();
+        IJavaElement je = (IJavaElement) input.getAdapter(IJavaElement.class);
+        if (je != null) {
+            IJavaProject jproject = je.getJavaProject();
+            if (jproject != null) {
+                launch(jproject, mode);
+            }
+        }
+    }
+
+    void launch(IJavaProject project, String mode) {
+        try {
+            ILaunchConfiguration config = find(project);
+            if (config == null)
+                config = createConfiguration(project);
+            
+            DebugUITools.launch(config, mode);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    ILaunchConfigurationType getConfigurationType() {
+        return getLaunchManager()
+                .getLaunchConfigurationType("aQute.bnd.launch");
+    }
+
+    ILaunchConfiguration find(IJavaProject project) throws CoreException {
+        List<ILaunchConfiguration> candidateConfigs = new ArrayList<ILaunchConfiguration>();
+        ILaunchConfiguration[] configs = DebugPlugin.getDefault()
+                .getLaunchManager().getLaunchConfigurations(
+                        getConfigurationType());
+
+        for (int i = 0; i < configs.length; i++) {
+            ILaunchConfiguration config = configs[i];
+            if (config
+                    .getAttribute(
+                            IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
+                            "").equals(project.getElementName())) { //$NON-NLS-1$
+                candidateConfigs.add(config);
+            }
+        }
+        
+        if (candidateConfigs.size() == 0) {
+        	return null;
+        }
+        // return the latest
+        return candidateConfigs.get(candidateConfigs.size() - 1);
+    }
+
+    protected ILaunchManager getLaunchManager() {
+        return DebugPlugin.getDefault().getLaunchManager();
+    }
+
+    protected ILaunchConfiguration createConfiguration(IJavaProject type)
+            throws Exception {
+        ILaunchConfiguration config = null;
+        ILaunchConfigurationWorkingCopy wc = null;
+        ILaunchConfigurationType configType = getConfigurationType();
+        wc = configType.newInstance(null, getLaunchManager()
+                .generateUniqueLaunchConfigurationNameFrom(
+                        type.getElementName()));
+        wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
+                type.getElementName());
+        config = wc.doSave();
+        return config;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/bnd.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/bnd.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/bnd.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1597 @@
+package aQute.bnd.main;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.maven.*;
+import aQute.bnd.service.*;
+import aQute.bnd.test.*;
+import aQute.lib.deployer.*;
+import aQute.lib.jardiff.*;
+import aQute.lib.osgi.*;
+import aQute.lib.osgi.eclipse.*;
+import aQute.lib.tag.*;
+import aQute.libg.generics.*;
+import aQute.libg.version.*;
+
+/**
+ * Utility to make bundles.
+ * 
+ * TODO Add Javadoc comment for this type.
+ * 
+ * @version $Revision: 1.7 $
+ */
+public class bnd extends Processor {
+    PrintStream     out        = System.out;
+    static boolean  exceptions = false;
+
+    static boolean  failok     = false;
+    private Project project;
+
+    public static void main(String args[]) {
+        bnd main = new bnd();
+        try {
+            main.run(args);
+            if (bnd.failok)
+                return;
+
+            System.exit(main.getErrors().size());
+
+        } catch (Exception e) {
+            System.err.println("Software error occurred " + e);
+            if (exceptions)
+                e.printStackTrace();
+        }
+        System.exit(-1);
+    }
+
+    void run(String[] args) throws Exception {
+        int cnt = 0;
+        for (int i = 0; i < args.length; i++) {
+            if ("-failok".equals(args[i])) {
+                failok = true;
+            } else if ("-exceptions".equals(args[i])) {
+                exceptions = true;
+            } else if ("-trace".equals(args[i])) {
+                setTrace(true);
+            } else if ("-pedantic".equals(args[i])) {
+                setPedantic(true);
+            } else if ("-base".equals(args[i])) {
+                setBase(new File(args[++i]).getAbsoluteFile());
+                if (!getBase().isDirectory()) {
+                    System.out.println("-base must be a valid directory");
+                }
+            } else if ("wrap".equals(args[i])) {
+                cnt++;
+                doWrap(args, ++i);
+                break;
+            } else if ("print".equals(args[i])) {
+                cnt++;
+                doPrint(args, ++i);
+                break;
+            } else if ("release".equals(args[i])) {
+                cnt++;
+                doRelease(args, ++i);
+                break;
+            } else if ("debug".equals(args[i])) {
+                cnt++;
+                debug(args, ++i);
+                break;
+            } else if ("view".equals(args[i])) {
+                cnt++;
+                doView(args, ++i);
+                break;
+            } else if ("build".equals(args[i])) {
+                cnt++;
+                doBuild(args, ++i);
+                break;
+            } else if ("patch".equals(args[i])) {
+                cnt++;
+                patch(args, ++i);
+                break;
+            } else if ("runtests".equals(args[i])) {
+                cnt++;
+                runtests(args, ++i);
+                break;
+            } else if ("xref".equals(args[i])) {
+                cnt++;
+                doXref(args, ++i);
+                break;
+            } else if ("eclipse".equals(args[i])) {
+                cnt++;
+                doEclipse(args, ++i);
+                break;
+            } else if ("repo".equals(args[i])) {
+                cnt++;
+                repo(args, ++i);
+                break;
+            } else if ("diff".equals(args[i])) {
+                cnt++;
+                doDiff(args, ++i);
+                break;
+            } else if ("test".equals(args[i])) {
+                cnt++;
+                test(args, ++i);
+                break;
+            } else if ("help".equals(args[i])) {
+                cnt++;
+                doHelp(args, ++i);
+                break;
+            } else if ("macro".equals(args[i])) {
+                cnt++;
+                doMacro(args, ++i);
+                break;
+            } else {
+                cnt++;
+                String path = args[i];
+                if (path.startsWith("-")) {
+                    doHelp(args, i);
+                    error("Invalid option on commandline: " + args[i]);
+                    break;
+                } else {
+                    if (path.endsWith(Constants.DEFAULT_BND_EXTENSION))
+                        doBuild(new File(path), new File[0], new File[0], null,
+                                "", new File(path).getParentFile(), 0,
+                                new HashSet<File>());
+                    else if (path.endsWith(Constants.DEFAULT_JAR_EXTENSION)
+                            || path.endsWith(Constants.DEFAULT_BAR_EXTENSION))
+                        doPrint(path, -1);
+                    else {
+                        try {
+                            i = doMacro(args, i);
+                        } catch (Throwable t) {
+                            t.printStackTrace();
+                            doHelp(args, i);
+                            error("Invalid commandline: " + args[i]);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (cnt == 0) {
+            File f = new File("bnd.bnd");
+            if (f.exists()) {
+                doBuild(f, new File[0], new File[0], null, "", f
+                        .getParentFile(), 0, new HashSet<File>());
+            } else {
+                doHelp();
+                error("No files on commandline");
+            }
+        }
+        int n = 1;
+        switch (getErrors().size()) {
+        case 0:
+            // System.err.println("No errors");
+            break;
+        case 1:
+            System.err.println("One error");
+            break;
+        default:
+            System.err.println(getErrors().size() + " errors");
+        }
+        for (String msg : getErrors()) {
+            System.err.println(n++ + " : " + msg);
+        }
+        n = 1;
+        switch (getWarnings().size()) {
+        case 0:
+            // System.err.println("No warnings");
+            break;
+        case 1:
+            System.err.println("One warning");
+            break;
+        default:
+            System.err.println(getWarnings().size() + " warnings");
+        }
+        for (String msg : getWarnings()) {
+            System.err.println(n++ + " : " + msg);
+        }
+    }
+
+    private int doMacro(String[] args, int i) throws Exception {
+        String result;
+        for (; i < args.length; i++) {
+            String cmd = args[i];
+            cmd = cmd.replaceAll("@\\{([^}])\\}", "\\${$1}");
+            cmd = cmd.replaceAll(":", ";");
+            cmd = cmd.replaceAll("[^$](.*)", "\\${$0}");
+            result = getProject().getReplacer().process(cmd);
+            if (result != null || result.length() != 0) {
+                Collection<String> parts = split(result);
+                for (String s : parts) {
+                    out.println(s);
+                }
+            } else
+                out.println("No result for " + cmd);
+
+        }
+        return i;
+    }
+
+    private void doRelease(String[] args, int i) throws Exception {
+        Project project = getProject();
+        project.release(false);
+        getInfo(project);
+    }
+
+    /**
+     * Cross reference every class in the jar file to the files it references
+     * 
+     * @param args
+     * @param i
+     */
+
+    private void doXref(String[] args, int i) {
+        for (; i < args.length; i++) {
+            try {
+                File file = new File(args[i]);
+                Jar jar = new Jar(file.getName(), file);
+                try {
+                    for (Map.Entry<String, Resource> entry : jar.getResources()
+                            .entrySet()) {
+                        String key = entry.getKey();
+                        Resource r = entry.getValue();
+                        if (key.endsWith(".class")) {
+                            InputStream in = r.openInputStream();
+                            Clazz clazz = new Clazz(key, r);
+                            out.print(key);
+                            Set<String> xref = clazz.parseClassFile();
+                            in.close();
+                            for (String element : xref) {
+                                out.print("\t");
+                                out.print(element);
+                            }
+                            out.println();
+                        }
+                    }
+                } finally {
+                    jar.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+    }
+
+    private void doEclipse(String[] args, int i) throws Exception {
+        File dir = new File("").getAbsoluteFile();
+        if (args.length == i)
+            doEclipse(dir);
+        else {
+            for (; i < args.length; i++) {
+                doEclipse(new File(dir, args[i]));
+            }
+        }
+    }
+
+    private void doEclipse(File file) throws Exception {
+        if (!file.isDirectory())
+            error("Eclipse requires a path to a directory: "
+                    + file.getAbsolutePath());
+        else {
+            File cp = new File(file, ".classpath");
+            if (!cp.exists()) {
+                error("Cannot find .classpath in project directory: "
+                        + file.getAbsolutePath());
+            } else {
+                EclipseClasspath eclipse = new EclipseClasspath(this, file
+                        .getParentFile(), file);
+                out.println("Classpath    " + eclipse.getClasspath());
+                out.println("Dependents   " + eclipse.getDependents());
+                out.println("Sourcepath   " + eclipse.getSourcepath());
+                out.println("Output       " + eclipse.getOutput());
+                out.println();
+            }
+        }
+
+    }
+
+    final static int BUILD_SOURCES = 1;
+    final static int BUILD_POM     = 2;
+    final static int BUILD_FORCE   = 4;
+
+    private void doBuild(String[] args, int i) throws Exception {
+        File classpath[] = new File[0];
+        File workspace = null;
+        File sourcepath[] = new File[0];
+        File output = null;
+        String eclipse = "";
+        int options = 0;
+
+        for (; i < args.length; i++) {
+            if ("-workspace".startsWith(args[i])) {
+                workspace = new File(args[++i]);
+            } else if ("-classpath".startsWith(args[i])) {
+                classpath = getClasspath(args[++i]);
+            } else if ("-sourcepath".startsWith(args[i])) {
+                String arg = args[++i];
+                String spaces[] = arg.split("\\s*,\\s*");
+                sourcepath = new File[spaces.length];
+                for (int j = 0; j < spaces.length; j++) {
+                    File f = new File(spaces[j]);
+                    if (!f.exists())
+                        error("No such sourcepath entry: "
+                                + f.getAbsolutePath());
+                    sourcepath[j] = f;
+                }
+            } else if ("-eclipse".startsWith(args[i])) {
+                eclipse = args[++i];
+            } else if ("-noeclipse".startsWith(args[i])) {
+                eclipse = null;
+            } else if ("-output".startsWith(args[i])) {
+                output = new File(args[++i]);
+            } else if ("-sources".startsWith(args[i])) {
+                options |= BUILD_SOURCES;
+            } else if ("-pom".startsWith(args[i])) {
+                options |= BUILD_POM;
+            } else if ("-force".startsWith(args[i])) {
+                options |= BUILD_FORCE;
+            } else {
+                if (args[i].startsWith("-"))
+                    error("Invalid option for bnd: " + args[i]);
+                else {
+                    File properties = new File(args[i]);
+
+                    if (!properties.exists())
+                        error("Cannot find bnd file: " + args[i]);
+                    else {
+                        if (workspace == null)
+                            workspace = properties.getParentFile();
+
+                        doBuild(properties, classpath, sourcepath, output,
+                                eclipse, workspace, options,
+                                new HashSet<File>());
+                    }
+                    output = null;
+                }
+            }
+        }
+
+    }
+
+    private File[] getClasspath(String string) {
+        String spaces[] = string.split("\\s*,\\s*");
+        File classpath[] = new File[spaces.length];
+        for (int j = 0; j < spaces.length; j++) {
+            File f = new File(spaces[j]);
+            if (!f.exists())
+                error("No such classpath entry: " + f.getAbsolutePath());
+            classpath[j] = f;
+        }
+        return classpath;
+    }
+
+    private void doBuild(File properties, File classpath[], File sourcepath[],
+            File output, String eclipse, File workspace, int options,
+            Set<File> building) throws Exception {
+
+        properties = properties.getAbsoluteFile();
+        if (building.contains(properties)) {
+            error("Circular dependency in pre build " + properties);
+            return;
+        }
+        building.add(properties);
+
+        Builder builder = new Builder();
+        try {
+            builder.setPedantic(isPedantic());
+            builder.setProperties(properties);
+
+            if (output == null) {
+                String out = builder.getProperty("-output");
+                if (out != null) {
+                    output = getFile(properties.getParentFile(), out);
+                    if (!output.getName().endsWith(".jar"))
+                        output.mkdirs();
+                } else
+                    output = properties.getAbsoluteFile().getParentFile();
+            }
+
+            String prebuild = builder.getProperty("-prebuild");
+            if (prebuild != null)
+                prebuild(prebuild, properties.getParentFile(), classpath,
+                        sourcepath, output, eclipse, workspace, options,
+                        building);
+
+            doEclipse(builder, properties, classpath, sourcepath, eclipse,
+                    workspace);
+
+            if ((options & BUILD_SOURCES) != 0)
+                builder.getProperties().setProperty("-sources", "true");
+
+            if (failok)
+                builder.setProperty(Analyzer.FAIL_OK, "true");
+            Jar jar = builder.build();
+            getInfo(builder);
+            if (getErrors().size() > 0 && !failok)
+                return;
+
+            String name = builder.getBsn() + DEFAULT_JAR_EXTENSION;
+
+            if (output.isDirectory())
+                output = new File(output, name);
+
+            if ((options & BUILD_POM) != 0)
+                builder.doPom(jar);
+
+            jar.setName(output.getName());
+
+            String msg = "";
+            if (!output.exists() || output.lastModified() <= jar.lastModified()
+                    || (options & BUILD_FORCE) != 0) {
+                jar.write(output);
+            } else {
+                msg = "(not modified)";
+            }
+            statistics(jar, output, msg);
+        } finally {
+            builder.close();
+        }
+    }
+
+    private void prebuild(String prebuild, File base, File[] classpath,
+            File[] sourcepath, File output, String eclipse2, File workspace,
+            int options, Set<File> building) throws Exception {
+
+        // Force the output a directory
+        if (output.isFile())
+            output = output.getParentFile();
+
+        Collection<String> parts = Processor.split(prebuild);
+        for (String part : parts) {
+            File f = new File(part);
+            if (!f.exists())
+                f = new File(base, part);
+            if (!f.exists()) {
+                error("Trying to build a non-existent file: " + parts);
+                continue;
+            }
+            try {
+                doBuild(f, classpath, sourcepath, output, eclipse2, workspace,
+                        options, building);
+            } catch (Exception e) {
+                error("Trying to build: " + part + " " + e);
+            }
+        }
+    }
+
+    private void statistics(Jar jar, File output, String msg) {
+        out.println(jar.getName() + " " + jar.getResources().size() + " "
+                + output.length() + msg);
+    }
+
+    /**
+     * @param properties
+     * @param classpath
+     * @param eclipse
+     * @return
+     * @throws IOException
+     */
+    void doEclipse(Builder builder, File properties, File[] classpath,
+            File sourcepath[], String eclipse, File workspace)
+            throws IOException {
+        if (eclipse != null) {
+            File project = new File(workspace, eclipse).getAbsoluteFile();
+            if (project.exists() && project.isDirectory()) {
+                try {
+
+                    EclipseClasspath path = new EclipseClasspath(this, project
+                            .getParentFile(), project);
+                    List<File> newClasspath = Create.copy(Arrays
+                            .asList(classpath));
+                    newClasspath.addAll(path.getClasspath());
+                    classpath = (File[]) newClasspath.toArray(classpath);
+
+                    List<File> newSourcepath = Create.copy(Arrays
+                            .asList(sourcepath));
+                    newSourcepath.addAll(path.getSourcepath());
+                    sourcepath = (File[]) newSourcepath.toArray(sourcepath);
+                } catch (Exception e) {
+                    if (eclipse.length() > 0)
+                        error("Eclipse specified (" + eclipse
+                                + ") but getting error processing: " + e);
+                }
+            } else {
+                if (eclipse.length() > 0)
+                    error("Eclipse specified (" + eclipse
+                            + ") but no project directory found");
+            }
+        }
+        builder.setClasspath(classpath);
+        builder.setSourcepath(sourcepath);
+    }
+
+    private void doHelp() {
+        doHelp(new String[0], 0);
+    }
+
+    private void doHelp(String[] args, int i) {
+        if (args.length <= i) {
+            out
+                    .println("bnd -failok? -exceptions? ( wrap | print | build | eclipse | xref | view )?");
+            out.println("See http://www.aQute.biz/Code/Bnd");
+        } else {
+            while (args.length > i) {
+                if ("wrap".equals(args[i])) {
+                    out
+                            .println("bnd wrap (-output <file|dir>)? (-properties <file>)? <jar-file>");
+                } else if ("print".equals(args[i])) {
+                    out
+                            .println("bnd wrap -verify? -manifest? -list? -eclipse <jar-file>");
+                } else if ("build".equals(args[i])) {
+                    out
+                            .println("bnd build (-output <file|dir>)? (-classpath <list>)? (-sourcepath <list>)? ");
+                    out
+                            .println("    -eclipse? -noeclipse? -sources? <bnd-file>");
+                } else if ("eclipse".equals(args[i])) {
+                    out.println("bnd eclipse");
+                } else if ("view".equals(args[i])) {
+                    out.println("bnd view <file.jar> <resource-names>+");
+                }
+                i++;
+            }
+        }
+    }
+
+    final static int VERIFY    = 1;
+
+    final static int MANIFEST  = 2;
+
+    final static int LIST      = 4;
+
+    final static int ECLIPSE   = 8;
+    final static int IMPEXP    = 16;
+    final static int USES      = 32;
+    final static int USEDBY    = 64;
+    final static int COMPONENT = 128;
+
+    static final int HEX       = 0;
+
+    private void doPrint(String[] args, int i) throws Exception {
+        int options = 0;
+
+        for (; i < args.length; i++) {
+            if ("-verify".startsWith(args[i]))
+                options |= VERIFY;
+            else if ("-manifest".startsWith(args[i]))
+                options |= MANIFEST;
+            else if ("-list".startsWith(args[i]))
+                options |= LIST;
+            else if ("-eclipse".startsWith(args[i]))
+                options |= ECLIPSE;
+            else if ("-impexp".startsWith(args[i]))
+                options |= IMPEXP;
+            else if ("-uses".startsWith(args[i]))
+                options |= USES;
+            else if ("-usedby".startsWith(args[i]))
+                options |= USEDBY;
+            else if ("-component".startsWith(args[i]))
+                options |= COMPONENT;
+            else if ("-all".startsWith(args[i]))
+                options = -1;
+            else {
+                if (args[i].startsWith("-"))
+                    error("Invalid option for print: " + args[i]);
+                else
+                    doPrint(args[i], options);
+            }
+        }
+    }
+
+    public void doPrint(String string, int options) throws Exception {
+        File file = new File(string);
+        if (!file.exists())
+            error("File to print not found: " + string);
+        else {
+            if (options == 0)
+                options = VERIFY | MANIFEST | IMPEXP | USES;
+
+            Jar jar = new Jar(file.getName(), file);
+            try {
+                if ((options & VERIFY) != 0) {
+                    Verifier verifier = new Verifier(jar);
+                    verifier.setPedantic(isPedantic());
+                    verifier.verify();
+                    getInfo(verifier);
+                }
+                if ((options & MANIFEST) != 0) {
+                    Manifest manifest = jar.getManifest();
+                    if (manifest == null)
+                        warning("JAR file has no manifest " + string);
+                    else {
+                        out.println("[MANIFEST " + jar.getName() + "]");
+                        SortedSet<String> sorted = new TreeSet<String>();
+                        for (Object element : manifest.getMainAttributes()
+                                .keySet()) {
+                            sorted.add(element.toString());
+                        }
+                        for (String key : sorted) {
+                            Object value = manifest.getMainAttributes()
+                                    .getValue(key);
+                            format("%-40s %-40s\r\n",
+                                    new Object[] { key, value });
+                        }
+                    }
+                    out.println();
+                }
+                if ((options & IMPEXP) != 0) {
+                    out.println("[IMPEXP]");
+                    Manifest m = jar.getManifest();
+                    if (m != null) {
+                        Map<String, Map<String, String>> imports = parseHeader(m
+                                .getMainAttributes().getValue(
+                                        Analyzer.IMPORT_PACKAGE));
+                        Map<String, Map<String, String>> exports = parseHeader(m
+                                .getMainAttributes().getValue(
+                                        Analyzer.EXPORT_PACKAGE));
+                        imports.keySet().removeAll(exports.keySet());
+                        print("Import-Package",
+                                new TreeMap<String, Map<String, String>>(
+                                        imports));
+                        print("Export-Package",
+                                new TreeMap<String, Map<String, String>>(
+                                        exports));
+                    } else
+                        warning("File has no manifest");
+                }
+
+                if ((options & (USES | USEDBY)) != 0) {
+                    out.println();
+                    Analyzer analyzer = new Analyzer();
+                    analyzer.setPedantic(isPedantic());
+                    analyzer.setJar(jar);
+                    analyzer.analyze();
+                    if ((options & USES) != 0) {
+                        out.println("[USES]");
+                        printMapOfSets(new TreeMap<String, Set<String>>(
+                                analyzer.getUses()));
+                        out.println();
+                    }
+                    if ((options & USEDBY) != 0) {
+                        out.println("[USEDBY]");
+                        printMapOfSets(invertMapOfCollection(analyzer.getUses()));
+                    }
+                }
+
+                if ((options & COMPONENT) != 0) {
+                    printComponents(out, jar);
+                }
+
+                if ((options & LIST) != 0) {
+                    out.println("[LIST]");
+                    for (Map.Entry<String, Map<String, Resource>> entry : jar
+                            .getDirectories().entrySet()) {
+                        String name = entry.getKey();
+                        Map<String, Resource> contents = entry.getValue();
+                        out.println(name);
+                        if (contents != null) {
+                            for (String element : contents.keySet()) {
+                                int n = element.lastIndexOf('/');
+                                if (n > 0)
+                                    element = element.substring(n + 1);
+                                out.print("  ");
+                                out.print(element);
+                                String path = element;
+                                if (name.length() != 0)
+                                    path = name + "/" + element;
+                                Resource r = contents.get(path);
+                                if (r != null) {
+                                    String extra = r.getExtra();
+                                    if (extra != null)
+                                        out.print(" extra='" + extra + "'");
+                                }
+                                out.println();
+                            }
+                        } else {
+                            out.println(name + " <no contents>");
+                        }
+                    }
+                    out.println();
+                }
+            } finally {
+                jar.close();
+            }
+        }
+    }
+
+    /**
+     * Print the components in this JAR file.
+     * 
+     * @param jar
+     */
+    private void printComponents(PrintStream out, Jar jar) throws IOException {
+        out.println("[COMPONENTS]");
+        Manifest manifest = jar.getManifest();
+        if (manifest == null) {
+            out.println("No manifest");
+            return;
+        }
+
+        String componentHeader = manifest.getMainAttributes().getValue(
+                Constants.SERVICE_COMPONENT);
+        Map<String, Map<String, String>> clauses = parseHeader(componentHeader);
+        for (String path : clauses.keySet()) {
+            // TODO
+            out.println(path);
+        }
+
+        out.println();
+    }
+
+    Map<String, Set<String>> invertMapOfCollection(Map<String, Set<String>> map) {
+        Map<String, Set<String>> result = new TreeMap<String, Set<String>>();
+        for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
+            String name = entry.getKey();
+            if (name.startsWith("java.") && !name.equals("java.sql"))
+                continue;
+
+            Collection<String> used = entry.getValue();
+            for (String n : used) {
+                if (n.startsWith("java.") && !n.equals("java.sql"))
+                    continue;
+                Set<String> set = result.get(n);
+                if (set == null) {
+                    set = new TreeSet<String>();
+                    result.put(n, set);
+                }
+                set.add(name);
+            }
+        }
+        return result;
+    }
+
+    void printMapOfSets(Map<String, Set<String>> map) {
+        for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
+            String name = entry.getKey();
+            Set<String> used = new TreeSet<String>(entry.getValue());
+
+            for (Iterator<String> k = used.iterator(); k.hasNext();) {
+                String n = (String) k.next();
+                if (n.startsWith("java.") && !n.equals("java.sql"))
+                    k.remove();
+            }
+            String list = vertical(40, used);
+            format("%-40s %s", new Object[] { name, list });
+        }
+    }
+
+    String vertical(int padding, Set<String> used) {
+        StringBuffer sb = new StringBuffer();
+        String del = "";
+        for (Iterator<String> u = used.iterator(); u.hasNext();) {
+            String name = (String) u.next();
+            sb.append(del);
+            sb.append(name);
+            sb.append("\r\n");
+            del = pad(padding);
+        }
+        if (sb.length() == 0)
+            sb.append("\r\n");
+        return sb.toString();
+    }
+
+    String pad(int i) {
+        StringBuffer sb = new StringBuffer();
+        while (i-- > 0)
+            sb.append(' ');
+        return sb.toString();
+    }
+
+    /**
+     * View files from JARs
+     * 
+     * We parse the commandline and print each file on it.
+     * 
+     * @param args
+     * @param i
+     * @throws Exception
+     */
+    private void doView(String[] args, int i) throws Exception {
+        int options = 0;
+        String charset = "UTF-8";
+        File output = null;
+
+        for (; i < args.length; i++) {
+            if ("-charset".startsWith(args[i]))
+                charset = args[++i];
+            else if ("-output".startsWith(args[i])) {
+                output = new File(args[++i]);
+            } else
+                break;
+        }
+
+        if (i >= args.length) {
+            error("Insufficient arguments for view, no JAR file");
+            return;
+        }
+        String jar = args[i++];
+        if (i >= args.length) {
+            error("No Files to view");
+            return;
+        }
+
+        doView(jar, args, i, charset, options, output);
+    }
+
+    private void doView(String jar, String[] args, int i, String charset,
+            int options, File output) {
+        File path = new File(jar).getAbsoluteFile();
+        File dir = path.getParentFile();
+        if (dir == null) {
+            dir = new File("");
+        }
+        if (!dir.exists()) {
+            error("No such file: " + dir.getAbsolutePath());
+            return;
+        }
+
+        String name = path.getName();
+        if (name == null)
+            name = "META-INF/MANIFEST.MF";
+
+        Instruction instruction = Instruction.getPattern(path.getName());
+
+        File[] children = dir.listFiles();
+        for (int j = 0; j < children.length; j++) {
+            String base = children[j].getName();
+            // out.println("Considering: " +
+            // children[j].getAbsolutePath() + " " +
+            // instruction.getPattern());
+            if (instruction.matches(base) ^ instruction.isNegated()) {
+                for (; i < args.length; i++) {
+                    doView(children[j], args[i], charset, options, output);
+                }
+            }
+        }
+    }
+
+    private void doView(File file, String resource, String charset,
+            int options, File output) {
+        // out.println("doView:" + file.getAbsolutePath() );
+        try {
+            Instruction instruction = Instruction.getPattern(resource);
+            FileInputStream fin = new FileInputStream(file);
+            ZipInputStream in = new ZipInputStream(fin);
+            ZipEntry entry = in.getNextEntry();
+            while (entry != null) {
+                // out.println("view " + file + ": "
+                // + instruction.getPattern() + ": " + entry.getName()
+                // + ": " + output + ": "
+                // + instruction.matches(entry.getName()));
+                if (instruction.matches(entry.getName())
+                        ^ instruction.isNegated())
+                    doView(entry.getName(), in, charset, options, output);
+                in.closeEntry();
+                entry = in.getNextEntry();
+            }
+            in.close();
+            fin.close();
+        } catch (Exception e) {
+            out.println("Can not process: " + file.getAbsolutePath());
+            e.printStackTrace();
+        }
+    }
+
+    private void doView(String name, ZipInputStream in, String charset,
+            int options, File output) throws Exception {
+        int n = name.lastIndexOf('/');
+        name = name.substring(n + 1);
+
+        InputStreamReader rds = new InputStreamReader(in, charset);
+        OutputStreamWriter wrt = new OutputStreamWriter(out);
+        if (output != null)
+            if (output.isDirectory())
+                wrt = new FileWriter(new File(output, name));
+            else
+                wrt = new FileWriter(output);
+
+        copy(rds, wrt);
+        // rds.close(); also closes the stream which closes our zip file it
+        // seems
+        if (output != null)
+            wrt.close();
+        else
+            wrt.flush();
+    }
+
+    private void copy(InputStreamReader rds, OutputStreamWriter wrt)
+            throws IOException {
+        char buffer[] = new char[1024];
+        int size = rds.read(buffer);
+        while (size > 0) {
+            wrt.write(buffer, 0, size);
+            size = rds.read(buffer);
+        }
+    }
+
+    private void print(String msg, Map<String, Map<String, String>> ports) {
+        if (ports.isEmpty())
+            return;
+        out.println(msg);
+        for (Map.Entry<String, Map<String, String>> entry : ports.entrySet()) {
+            String key = entry.getKey();
+            Map<String, String> clause = Create.copy(entry.getValue());
+            clause.remove("uses:");
+            format("  %-38s %s\r\n", key.trim(), clause.isEmpty() ? "" : clause
+                    .toString());
+        }
+    }
+
+    private void format(String string, Object... objects) {
+        if (objects == null || objects.length == 0)
+            return;
+
+        StringBuffer sb = new StringBuffer();
+        int index = 0;
+        for (int i = 0; i < string.length(); i++) {
+            char c = string.charAt(i);
+            switch (c) {
+            case '%':
+                String s = objects[index++] + "";
+                int width = 0;
+                int justify = -1;
+
+                i++;
+
+                c = string.charAt(i++);
+                switch (c) {
+                case '-':
+                    justify = -1;
+                    break;
+                case '+':
+                    justify = 1;
+                    break;
+                case '|':
+                    justify = 0;
+                    break;
+                default:
+                    --i;
+                }
+                c = string.charAt(i++);
+                while (c >= '0' && c <= '9') {
+                    width *= 10;
+                    width += c - '0';
+                    c = string.charAt(i++);
+                }
+                if (c != 's') {
+                    throw new IllegalArgumentException(
+                            "Invalid sprintf format:  " + string);
+                }
+
+                if (s.length() > width)
+                    sb.append(s);
+                else {
+                    switch (justify) {
+                    case -1:
+                        sb.append(s);
+                        for (int j = 0; j < width - s.length(); j++)
+                            sb.append(" ");
+                        break;
+
+                    case 1:
+                        for (int j = 0; j < width - s.length(); j++)
+                            sb.append(" ");
+                        sb.append(s);
+                        break;
+
+                    case 0:
+                        int spaces = (width - s.length()) / 2;
+                        for (int j = 0; j < spaces; j++)
+                            sb.append(" ");
+                        sb.append(s);
+                        for (int j = 0; j < width - s.length() - spaces; j++)
+                            sb.append(" ");
+                        break;
+                    }
+                }
+                break;
+
+            default:
+                sb.append(c);
+            }
+        }
+        out.print(sb);
+    }
+
+    private void doWrap(String[] args, int i) throws Exception {
+        int options = 0;
+        File properties = null;
+        File output = null;
+        File classpath[] = null;
+        for (; i < args.length; i++) {
+            if ("-output".startsWith(args[i]))
+                output = new File(args[++i]);
+            else if ("-properties".startsWith(args[i]))
+                properties = new File(args[++i]);
+            else if ("-classpath".startsWith(args[i])) {
+                classpath = getClasspath(args[++i]);
+            } else {
+                File bundle = new File(args[i]);
+                doWrap(properties, bundle, output, classpath, options, null);
+            }
+        }
+    }
+
+    public boolean doWrap(File properties, File bundle, File output,
+            File classpath[], int options, Map<String, String> additional)
+            throws Exception {
+        if (!bundle.exists()) {
+            error("No such file: " + bundle.getAbsolutePath());
+            return false;
+        } else {
+            Analyzer analyzer = new Analyzer();
+            try {
+                analyzer.setPedantic(isPedantic());
+                analyzer.setJar(bundle);
+                Jar dot = analyzer.getJar();
+
+                if (properties != null) {
+                    analyzer.setProperties(properties);
+                }
+                if (additional != null)
+                    analyzer.putAll(additional, false);
+
+                if (analyzer.getProperty(Analyzer.IMPORT_PACKAGE) == null)
+                    analyzer.setProperty(Analyzer.IMPORT_PACKAGE,
+                            "*;resolution:=optional");
+
+                if (analyzer.getProperty(Analyzer.BUNDLE_SYMBOLICNAME) == null) {
+                    Pattern p = Pattern.compile("("
+                            + Verifier.SYMBOLICNAME.pattern()
+                            + ")(-[0-9])?.*\\.jar");
+                    String base = bundle.getName();
+                    Matcher m = p.matcher(base);
+                    base = "Untitled";
+                    if (m.matches()) {
+                        base = m.group(1);
+                    } else {
+                        error("Can not calculate name of output bundle, rename jar or use -properties");
+                    }
+                    analyzer.setProperty(Analyzer.BUNDLE_SYMBOLICNAME, base);
+                }
+
+                if (analyzer.getProperty(Analyzer.EXPORT_PACKAGE) == null) {
+                    String export = analyzer.calculateExportsFromContents(dot);
+                    analyzer.setProperty(Analyzer.EXPORT_PACKAGE, export);
+                }
+
+                if (classpath != null)
+                    analyzer.setClasspath(classpath);
+
+                analyzer.mergeManifest(dot.getManifest());
+
+                //
+                // Cleanup the version ..
+                //
+                String version = analyzer.getProperty(Analyzer.BUNDLE_VERSION);
+                if (version != null) {
+                    version = Builder.cleanupVersion(version);
+                    analyzer.setProperty(Analyzer.BUNDLE_VERSION, version);
+                }
+
+                if (output == null)
+                    if (properties != null)
+                        output = properties.getAbsoluteFile().getParentFile();
+                    else
+                        output = bundle.getAbsoluteFile().getParentFile();
+
+                String path = bundle.getName();
+                if (path.endsWith(DEFAULT_JAR_EXTENSION))
+                    path = path.substring(0, path.length()
+                            - DEFAULT_JAR_EXTENSION.length())
+                            + DEFAULT_BAR_EXTENSION;
+                else
+                    path = bundle.getName() + DEFAULT_BAR_EXTENSION;
+
+                if (output.isDirectory())
+                    output = new File(output, path);
+
+                analyzer.calcManifest();
+                Jar jar = analyzer.getJar();
+                getInfo(analyzer);
+                statistics(jar, output, "");
+                File f = File.createTempFile("tmpbnd", ".jar");
+                jar.write(f);
+                jar.close();
+                if (!f.renameTo(output)) {
+                    copy(f, output);
+                    f.deleteOnExit();
+                }
+                return getErrors().size() == 0;
+            } finally {
+                analyzer.close();
+            }
+        }
+    }
+
+
+    void doDiff(String args[], int first) throws IOException {
+        File base = new File("");
+        boolean strict = false;
+        Jar targets[] = new Jar[2];
+        int n = 0;
+
+        for (int i = first; i < args.length; i++) {
+            if ("-d".equals(args[i]))
+                base = getFile(base, args[++i]);
+            else if ("-strict".equals(args[i]))
+                strict = "true".equalsIgnoreCase(args[++i]);
+            else if (args[i].startsWith("-"))
+                error("Unknown option for diff: " + args[i]);
+            else {
+                if (n >= 2)
+                    System.err.println("Must have 2 files ... not more");
+                else {
+                    File f = getFile(base, args[i]);
+                    if (!f.isFile())
+                        System.err.println("Not a file: " + f);
+                    else {
+                        try {
+                            Jar jar = new Jar(f);
+                            targets[n++] = jar;
+                        } catch (Exception e) {
+                            System.err.println("Not a JAR file: " + f);
+                        }
+                    }
+                }
+            }
+        }
+        if (n != 2) {
+            System.err.println("Must have 2 files ...");
+            return;
+        }
+        Diff diff = new Diff();
+
+        Map<String, Object> map = diff.diff(targets[0], targets[1], strict);
+        diff.print(System.out, map, 0);
+
+        for (Jar jar : targets) {
+            jar.close();
+        }
+        diff.close();
+    }
+
+    void copy(File a, File b) {
+        try {
+            InputStream in = new FileInputStream(a);
+            OutputStream out = new FileOutputStream(b);
+            byte[] buffer = new byte[8196];
+            int size = in.read(buffer);
+            while (size > 0) {
+                out.write(buffer, 0, size);
+                size = in.read(buffer);
+            }
+            in.close();
+            out.close();
+        } catch (IOException e) {
+            error("While copying the output file: " + a + "->" + b);
+        }
+    }
+
+    public void setOut(PrintStream out) {
+        this.out = out;
+    }
+
+    /**
+     * Run a test
+     * 
+     * @throws Exception
+     */
+
+    public void test(String args[], int i) throws Exception {
+        Project project = getProject();
+        project.test();
+        getInfo(project);
+    }
+
+    public Project getProject() throws Exception {
+        if (project != null)
+            return project;
+
+        project = Workspace.getProject(getBase());
+        if (!project.isValid())
+            throw new IllegalArgumentException("The base directory "
+                    + getBase() + " is not a project directory ");
+
+        return project;
+    }
+
+    /**
+     * Printout all the variables.
+     * 
+     * @param args
+     * @param i
+     * @throws Exception
+     */
+    public void debug(String args[], int i) throws Exception {
+        Project project = getProject();
+        System.out.println("Project: " + project);
+        Properties p = project.getFlattenedProperties();
+        for (Object k : p.keySet()) {
+            String key = (String) k;
+            String s = p.getProperty(key);
+            Collection<String> l = null;
+
+            if (s.indexOf(',') > 0)
+                l = split(s);
+            else if (s.indexOf(':') > 0)
+                l = split(s, "\\s*:\\s*");
+            if (l != null) {
+                String del = key;
+                for (String ss : l) {
+                    System.out.printf("%-40s %s\n", del, ss);
+                    del = "";
+                }
+            } else
+                System.out.printf("%-40s %s\n", key, s);
+        }
+    }
+
+    /**
+     * Manage the repo.
+     * 
+     * <pre>
+     *  repo
+     *      list
+     *      put &lt;file|url&gt;
+     *      get &lt;bsn&gt; (&lt;version&gt;)?
+     *      fetch &lt;file|url&gt;
+     * </pre>
+     */
+
+    public void repo(String args[], int i) throws Exception {
+        String bsn = null;
+        String version = null;
+
+        Project p = Workspace.getProject(getBase());
+        List<RepositoryPlugin> repos = p.getPlugins(RepositoryPlugin.class);
+        RepositoryPlugin writable = null;
+        for (Iterator<RepositoryPlugin> rp = repos.iterator(); rp.hasNext();) {
+            RepositoryPlugin rpp = rp.next();
+            if (rpp.canWrite()) {
+                writable = rpp;
+                break;
+            }
+        }
+
+        for (; i < args.length; i++) {
+            if ("repos".equals(args[i])) {
+                int n = 0;
+                for (RepositoryPlugin repo : repos) {
+                    out.printf("%3d. %s\n", n++, repo);
+                }
+            } else if ("list".equals(args[i])) {
+                String mask = null;
+                if (i < args.length - 1) {
+                    mask = args[++i];
+                }
+                repoList(repos, mask);
+            } else if ("-repo".equals(args[i])) {
+                String location = args[++i];
+                if (location.equals("maven")) {
+                    System.out.println("Maven");
+                    MavenRepository maven = new MavenRepository();
+                    maven.setProperties(new HashMap<String, String>());
+                    maven.setReporter(this);
+                    repos = Arrays.asList((RepositoryPlugin) maven);
+                } else {
+                    FileRepo repo = new FileRepo();
+                    repo.setReporter(this);
+                    repo.setLocation(location);
+                    repos = Arrays.asList((RepositoryPlugin) repo);
+                    writable = repo;
+                }
+            } else if ("-bsn".equals(args[i])) {
+                bsn = args[++i];
+            } else if ("-version".equals(args[i])) {
+                version = args[++i];
+            } else if ("put".equals(args[i]))
+                repoPut(writable, p, args[++i], bsn, version);
+            else if ("get".equals(args[i]))
+                repoGet(repos, args[++i]);
+            else
+                repoFetch(repos, args[++i]);
+        }
+    }
+
+    private void repoGet(List<RepositoryPlugin> writable, String string) {
+
+    }
+
+    private void repoPut(RepositoryPlugin writable, Project project,
+            String file, String bsn, String version) throws Exception {
+        File f = getFile(file);
+        if (f.isFile()) {
+
+            Jar jar = project.getValidJar(f);
+            Manifest manifest = jar.getManifest();
+            if (bsn != null)
+                manifest.getMainAttributes().putValue(
+                        Constants.BUNDLE_SYMBOLICNAME, bsn);
+            if (version != null)
+                manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION,
+                        version);
+
+            writable.put(jar);
+
+        } else
+            error("There is no such file: " + f.getAbsolutePath());
+    }
+
+    private void repoFetch(List<RepositoryPlugin> repos, String string) {
+        File f = getFile(string);
+        if (f.isFile()) {
+        } else {
+            // try {
+            // URL url = new URL(string);
+            // } catch (MalformedURLException mue) {
+            //
+            // }
+        }
+
+    }
+
+    void repoList(List<RepositoryPlugin> repos, String mask) throws Exception {
+        trace("list repo " + repos + " " + mask);
+        Set<String> bsns = new TreeSet<String>();
+        for (RepositoryPlugin repo : repos) {
+            bsns.addAll(repo.list(mask));
+        }
+
+        for (String bsn : bsns) {
+            Set<Version> versions = new TreeSet<Version>();
+            for (RepositoryPlugin repo : repos) {
+                List<Version> result = repo.versions(bsn);
+                if (result != null)
+                    versions.addAll(result);
+            }
+            out.printf("%-40s %s\n", bsn, versions);
+        }
+    }
+
+    /**
+     * Patch
+     */
+
+    void patch(String args[], int i) throws Exception {
+        for (; i < args.length; i++) {
+            if ("create".equals(args[i]) && i + 3 < args.length) {
+                createPatch(args[++i], args[++i], args[++i]);
+            } else if ("apply".equals(args[i]) && i + 3 < args.length) {
+                applyPatch(args[++i], args[++i], args[++i]);
+            } else if ("help".equals(args[i])) {
+                out
+                        .println("patch (create <old> <new> <patch> | patch <old> <patch> <new>)");
+            } else
+                out.println("Patch does not recognize command? "
+                        + Arrays.toString(args));
+        }
+    }
+
+    void createPatch(String old, String newer, String patch) throws Exception {
+        Jar a = new Jar(new File(old));
+        Manifest am = a.getManifest();
+        Jar b = new Jar(new File(newer));
+        Manifest bm = b.getManifest();
+
+        Set<String> delete = newSet();
+
+        for (String path : a.getResources().keySet()) {
+            Resource br = b.getResource(path);
+            if (br == null) {
+                trace("DELETE    %s", path);
+                delete.add(path);
+            } else {
+                Resource ar = a.getResource(path);
+                if (isEqual(ar, br)) {
+                    trace("UNCHANGED %s", path);
+                    b.remove(path);
+                } else
+                    trace("UPDATE    %s", path);
+            }
+        }
+
+        bm.getMainAttributes().putValue("Patch-Delete", join(delete, ", "));
+        bm.getMainAttributes().putValue("Patch-Version",
+                am.getMainAttributes().getValue("Bundle-Version"));
+
+        b.write(new File(patch));
+        a.close();
+        a.close();
+    }
+
+    private boolean isEqual(Resource ar, Resource br) throws Exception {
+        InputStream ain = ar.openInputStream();
+        try {
+            InputStream bin = br.openInputStream();
+            try {
+                while (true) {
+                    int an = ain.read();
+                    int bn = bin.read();
+                    if (an == bn) {
+                        if (an == -1)
+                            return true;
+                    } else
+                        return false;
+                }
+            } finally {
+                bin.close();
+            }
+        } finally {
+            ain.close();
+        }
+    }
+
+    void applyPatch(String old, String patch, String newer) throws Exception {
+        Jar a = new Jar(new File(old));
+        Jar b = new Jar(new File(patch));
+        Manifest bm = b.getManifest();
+
+        String patchDelete = bm.getMainAttributes().getValue("Patch-Delete");
+        String patchVersion = bm.getMainAttributes().getValue("Patch-Version");
+        if (patchVersion == null) {
+            error("To patch, you must provide a patch bundle.\nThe given "
+                    + patch
+                    + " bundle does not contain the Patch-Version header");
+            return;
+        }
+
+        Collection<String> delete = split(patchDelete);
+        Set<String> paths = new HashSet<String>(a.getResources().keySet());
+        paths.removeAll(delete);
+
+        for (String path : paths) {
+            Resource br = b.getResource(path);
+            if (br == null)
+                b.putResource(path, a.getResource(path));
+        }
+
+        bm.getMainAttributes().putValue("Bundle-Version", patchVersion);
+        b.write(new File(newer));
+        a.close();
+        b.close();
+    }
+
+    /**
+     * Run the tests from a prepared bnd file.
+     * 
+     * @param args
+     * @param i
+     * @throws Exception
+     */
+    public void runtests(String args[], int i) throws Exception {
+        int errors = 0;
+        File cwd = new File("").getAbsoluteFile();
+        Workspace ws = Workspace.getWorkspace(cwd);
+        File reportDir = getFile("reports");
+
+        Tag summary = new Tag("summary");
+        summary.addAttribute("date", new Date());
+        summary.addAttribute("ws", ws.getBase());
+
+        boolean hadOne = false;
+        for (; i < args.length; i++) {
+            if (args[i].startsWith("-reportdir")) {
+                reportDir = getFile(args[i]).getAbsoluteFile();
+                if ( reportDir.isFile() )
+                    error("-reportdir must be a directory");
+            } else if (args[i].startsWith("-title")) {
+                summary.addAttribute("title", args[++i]);
+            } else if (args[i].startsWith("-dir")) {
+                cwd = getFile(args[++i]).getAbsoluteFile();
+            } else {
+                File f = getFile(args[i]);
+                errors += runtTest(f, ws, reportDir, summary);
+                hadOne = true;
+            }
+        }
+
+        if (!hadOne) {
+            // See if we had any, if so, just use all files in
+            // the current directory
+            File[] files = cwd.listFiles();
+            for (File f : files) {
+                if (f.getName().endsWith(".bnd"))
+                    errors += runtTest(f, ws, reportDir, summary);
+            }
+        }
+
+        if (errors > 0)
+            summary.addAttribute("errors", errors);
+
+        File r = getFile(reportDir + "/summary.xml");
+        FileOutputStream out = new FileOutputStream(r);
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
+        try {
+            summary.print(0, pw);
+        } finally {
+            pw.close();
+            out.close();
+        }
+    }
+
+    private int runtTest(File testFile, Workspace ws, File reportDir,
+            Tag summary) throws Exception {
+        System.out.println("Report dir=" + reportDir + " testFile=" + testFile);
+        reportDir.mkdirs();
+        int errors = -1;
+        Tag report = new Tag("report");
+        summary.addContent(report);
+
+        report.addAttribute("path", testFile.getAbsolutePath());
+        if (!testFile.isFile()) {
+            error("No bnd file: " + testFile);
+            report.addAttribute("exception", "No bnd file found");
+        } else {
+            long start = System.currentTimeMillis();
+            errors = runtTests(report, ws, reportDir, testFile);
+
+            long duration = System.currentTimeMillis() - start;
+            report.addAttribute("duration", (duration + 500) / 1000);
+        }
+        return errors;
+    }
+
+    private int runtTests(Tag report, Workspace ws, File reportDir, File f)
+            throws Exception {
+        int errors = -1;
+        Project p = new Project(ws, f.getAbsoluteFile().getParentFile(), f
+                .getAbsoluteFile());
+        ProjectLauncher pl = new ProjectLauncher(p);
+        String t = p.getProperty("-target");
+        if (t == null) {
+            error("No target set for " + f);
+            report.addAttribute("exception", "No -target property found");
+        } else {
+            List<Container> targets = p.getBundles(Constants.STRATEGY_HIGHEST,
+                    t);
+            if (targets.size() != 1) {
+                error("Only one -target supported " + t);
+                report.addAttribute("exception", "Only one -target supported "
+                        + t);
+            } else {
+
+                for (Container c : targets) {
+                    File target = c.getFile();
+
+                    if (!target.isFile())
+                        error("The target is not a proper JAR file: " + target);
+                    else {
+                        report.addAttribute("title", f.getName().replace(".bnd", ""));
+                        report.addAttribute("id", target.getName());
+                        errors = runtTests(report, reportDir, f, pl, target);
+                    }
+                }
+            }
+        }
+        return errors;
+    }
+
+    private int runtTests(Tag report, File reportDir, File f,
+            ProjectLauncher pl, File target) throws Exception {
+        int errors;
+        String path = f.getName().replace(".bnd", "") + ".xml";
+        pl.setReport( getFile(reportDir , path));
+        report.addAttribute("report", path);
+
+        errors = pl.run(target);
+        
+        getInfo(pl);
+        if (errors == 0) {
+            trace("ok");
+        } else {
+            report.addAttribute("error", errors);
+            error("Failed: " + normalize(f) + ", " + errors + " test"
+                    + (errors > 1 ? "s" : "") + " failures, see "
+                    + normalize(pl.getTestreport().getAbsolutePath()));
+        }
+        return errors;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/packageinfo
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/packageinfo	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/main/packageinfo	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1 @@
+version 0.9

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/AnnotationReader.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/AnnotationReader.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/AnnotationReader.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,233 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.regex.*;
+
+import aQute.bnd.annotation.*;
+import aQute.bnd.make.ComponentDef.Reference;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class AnnotationReader extends ClassDataCollector {
+    StringBuilder       sb                     = new StringBuilder();
+    String              method;
+    String              methodDescriptor;
+    String              className;
+    Clazz               clazz;
+    String              interfaces[];
+    
+    ComponentDef        def                    = new ComponentDef();
+
+    final static String COMPONENT_ANNOTATION   = "L"
+                                                       + Component.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+    final static String REFERENCE_ANNOTATION   = "L"
+                                                       + aQute.bnd.annotation.Reference.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+    final static String UNREFERENCE_ANNOTATION = "L"
+                                                       + Unreference.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+    final static String ACTIVATE_ANNOTATION    = "L"
+                                                       + Activate.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+    final static String DEACTIVATE_ANNOTATION  = "L"
+                                                       + Deactivate.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+    final static String MODIFIED_ANNOTATION    = "L"
+                                                       + Modified.class
+                                                               .getName()
+                                                               .replace('.',
+                                                                       '/')
+                                                       + ";";
+
+    
+       
+    
+    
+    public ComponentDef getComponent() throws Exception {
+        clazz.parseClassFileWithCollector(this);
+        if (def.name != null)
+            return def;
+
+        return null;
+    }
+
+    public static ComponentDef getComponentDef(Clazz clazz) throws Exception {
+        AnnotationReader car = new AnnotationReader(clazz);
+        return car.getComponent();
+    }
+
+    public static ComponentDef getComponentDef(File f) throws Exception {
+        Resource r = new FileResource(f);
+        Clazz c = new Clazz("", r);
+        AnnotationReader car = new AnnotationReader(c);
+        return car.getComponent();
+    }
+
+    AnnotationReader(Clazz clazz) {
+        this.clazz = clazz;
+    }
+
+    public void annotation(Annotation annotation) {
+
+        if (annotation.getName().equals(COMPONENT_ANNOTATION)) {
+            def.name = annotation.get("name");
+            def.factory = annotation.get("factory");
+            def.enabled = annotation.get("enabled");
+            def.immediate = annotation.get("immediate");
+
+            String cp = (String) annotation.get("configurationPolicy");
+            if (cp != null) {
+                def.configurationPolicy = cp.toLowerCase();
+            }
+
+            if (def.name == null) {
+                def.name = className;
+            }
+            def.implementation = className.replace('/', '.');
+
+            Object[] provides = (Object[]) annotation.get("provides");
+            if (provides == null) {
+                def.provides = interfaces;
+            } else {
+                def.provides = new String[provides.length];
+                for (int i = 0; provides != null && i < provides.length; i++)
+                    def.provides[i] = descriptorToFQN(provides[i].toString());
+            }
+        } else if (annotation.getName().equals(ACTIVATE_ANNOTATION)) {
+            def.activate = method;
+        } else if (annotation.getName().equals(DEACTIVATE_ANNOTATION)) {
+            def.deactivate = method;
+        } else if (annotation.getName().equals(MODIFIED_ANNOTATION)) {
+            def.modified = method;
+        } else if (annotation.getName().equals(REFERENCE_ANNOTATION)) {
+            ComponentDef.Reference ref = getReference(annotation, method);
+            ref.multiple = (Boolean) annotation.get("multiple");
+            ref.optional = (Boolean) annotation.get("optional");
+            ref.dynamic = (Boolean) annotation.get("dynamic");
+
+            Integer c = annotation.get("type");
+            if (c != null) {
+                switch (c) {
+                case 0:
+                    break;
+                case '?':
+                    ref.multiple = false;
+                    ref.optional = true;
+                    ref.dynamic = true;
+                    break;
+                case '*':
+                    ref.multiple = true;
+                    ref.optional = true;
+                    ref.dynamic = true;
+                    break;
+                case '+':
+                    ref.multiple = true;
+                    ref.optional = false;
+                    ref.dynamic = true;
+                    break;
+
+                case '1':
+                    ref.multiple = false;
+                    ref.optional = false;
+                    ref.dynamic = false;
+                    break;
+                case '~':
+                    ref.multiple = false;
+                    ref.optional = true;
+                    ref.dynamic = false;
+                    break;
+                }
+            }
+            ref.target = (String) annotation.get("target");
+            ref.bind = method;
+            setService(ref, methodDescriptor);
+        } else if (annotation.getName().equals(UNREFERENCE_ANNOTATION)) {
+            ComponentDef.Reference ref = getReference(annotation, method);
+            ref.unbind = method;
+            setService(ref, methodDescriptor);
+        }
+    }
+
+    /**
+     * Skip L and ; and replace / for . in an object descriptor.
+     * 
+     * A string like Lcom/acme/Foo; becomes com.acme.Foo
+     * 
+     * @param string
+     * @return
+     */
+
+    private String descriptorToFQN(String string) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 1; i < string.length() - 1; i++) {
+            char c = string.charAt(i);
+            if (c == '/')
+                c = '.';
+            sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    static Pattern BINDDESCRIPTOR = Pattern.compile("\\(L(.*);\\)V");
+    static Pattern BINDMETHOD     = Pattern
+                                          .compile("(unset|set|bind|unbind)?(.)(.*)");
+
+    private Reference getReference(Annotation annotation, String method) {
+        String name = (String) annotation.get("name");
+        if (name == null) {
+            Matcher m = BINDMETHOD.matcher(method);
+            if (m.matches()) {
+                name = m.group(2).toLowerCase() + m.group(3);
+            } else
+                name = method;
+        }
+        return def.newReference(name);
+    }
+
+    void setService(ComponentDef.Reference ref, String descriptor) {
+        Matcher m = BINDDESCRIPTOR.matcher(descriptor);
+        if (m.matches()) {
+            String s = m.group(1).replace('/', '.');
+            if (ref.service == null)
+                ref.service = s;
+            else if (!ref.service.equals(s)) {
+                throw new IllegalArgumentException(
+                        "the bind and unbind method use different types for "
+                                + ref.name);
+            }
+        }
+    }
+
+    @Override
+    public void classBegin(int access, String name) {
+        className = name;
+    }
+
+    @Override
+    public void implementsInterfaces(String[] interfaces) {
+        this.interfaces = interfaces;
+    }
+
+    @Override
+    public void method(int access, String name, String descriptor) {
+        this.method = name;
+        this.methodDescriptor = descriptor;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ComponentDef.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ComponentDef.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ComponentDef.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,207 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.*;
+
+import aQute.lib.filter.*;
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
+
+public class ComponentDef extends Processor {
+    class Reference {
+        Boolean multiple;
+        Boolean optional;
+        Boolean dynamic;
+        String  target;
+        String  name;
+        String  bind;
+        String  unbind;
+        String  service;
+    }
+
+    String              name;
+    String              implementation;
+    String[]            provides;
+    String              activate;
+    String              deactivate;
+    String              modified;
+    String              factory;
+    Boolean             enabled;
+    Boolean             immediate;
+    String              configurationPolicy;
+    List<Reference>     references = new ArrayList<Reference>();
+    Version             version;
+    Boolean             serviceFactory;
+    Map<String, Object> properties;
+
+    public Reference newReference(String name) {
+        for (ComponentDef.Reference ref : references) {
+            if (ref.name.equals(name))
+                return ref;
+        }
+        Reference ref = new Reference();
+        ref.name = name;
+        references.add(ref);
+        return ref;
+    }
+
+    public void append(Appendable a) throws IOException {
+        String namespace = getNamespace();
+        a.append("<?xml version='1.0' encoding='utf-8'?>\n");
+        a.append("<component name='" + name + "'");
+        doAttribute(a, "\n   xmlns", namespace);
+
+        doAttribute(a, "factory", factory);
+        doAttribute(a, "immediate", immediate, "false", "true");
+        doAttribute(a, "enabled", enabled, "true", "false");
+        if (configurationPolicy != null
+                && !"optional".equals(configurationPolicy))
+            doAttribute(a, "configuration-policy", configurationPolicy,
+                    "optional", "require", "ignore");
+        doAttribute(a, "activate", activate, ServiceComponent.JIDENTIFIER);
+        doAttribute(a, "deactivate", deactivate, ServiceComponent.JIDENTIFIER);
+        doAttribute(a, "modified", modified, ServiceComponent.JIDENTIFIER);
+
+        a.append(">\n");
+
+        // Allow override of the implementation when people
+        // want to choose their own name
+        if (implementation == null)
+            error("No Implementation specified in component " + name);
+        else {
+            a.append("  <implementation");
+            doAttribute(a, "class", implementation);
+            a.append("/>\n");
+        }
+
+        if (provides != null) {
+            a.append("  <service");
+            doAttribute(a, "servicefactory", serviceFactory, "true", "false");
+            a.append(">\n");
+            for (String provide : provides) {
+                a.append("    <provide");
+                doAttribute(a, "interface", provide);
+                a.append(">\n");
+            }
+            a.append("  </service>\n");
+        }
+
+        if (properties != null) {
+            for (Map.Entry<String, Object> entry : properties.entrySet()) {
+                property(a, entry.getKey(), entry.getValue());
+            }
+        }
+
+        for (Reference ref : references) {
+            a.append("  <reference");
+            doAttribute(a, "name", ref.name);
+
+            doAttribute(a, "interface", ref.service);
+
+            if (ref.optional == null)
+                ref.optional = false;
+            if (ref.multiple == null)
+                ref.multiple = false;
+
+            String cardinality = ref.optional ? "0" : "1";
+            cardinality += "..";
+            cardinality += ref.multiple ? "n" : "1";
+
+            if (!cardinality.equals("1..1"))
+                doAttribute(a, "cardinality", cardinality);
+
+            doAttribute(a, "bind", ref.bind, ServiceComponent.JIDENTIFIER);
+            doAttribute(a, "unbind", ref.unbind, ServiceComponent.JIDENTIFIER);
+
+            if (ref.dynamic != null && ref.dynamic) {
+                doAttribute(a, "policy", "dynamic");
+            }
+
+            if (ref.target != null) {
+                // Filter filter = new Filter(ref.target);
+                // if (filter.verify() == null)
+                // doAttribute(a, "target", filter.toString());
+                // else
+                // error("Target for " + ref.name
+                // + " is not a correct filter: " + ref.target + " "
+                // + filter.verify());
+                doAttribute(a, "target", ref.target.trim());
+            }
+            a.append("/>\n");
+        }
+        a.append("</component>\n");
+    }
+
+    private void property(Appendable a, String key, Object value)
+            throws IOException {
+        a.append("<property");
+        doAttribute(a, "name", key);
+
+        boolean array = false;
+        Class<?> type = value.getClass();
+        if (type.isArray()) {
+            type = type.getComponentType();
+            array = true;
+        }
+
+        if (type != String.class)
+            doAttribute(a, "type", type.getName());
+
+        if (array) {
+            a.append(">\n");
+            Object parts[] = (Object[]) value;
+            for (Object part : parts) {
+                a.append(part.toString());
+                a.append("\n");
+            }
+            a.append("</property>");
+        } else {
+            doAttribute(a, "value", value);
+            a.append("/>\n");
+        }
+    }
+
+    private void doAttribute(Appendable pw, String name, Object value,
+            String... matches) throws IOException {
+        if (value != null) {
+            String v = value.toString();
+            if (matches.length != 0) {
+                if (matches.length == 1
+                        && matches[0].equals(ServiceComponent.JIDENTIFIER)) {
+                    if (!Verifier.isIdentifier(v))
+                        error(
+                                "Component attribute %s has value %s but is not a Java identifier",
+                                name, v);
+                } else {
+
+                    if (!Verifier.isMember(v, matches))
+                        error(
+                                "Component attribute %s has value %s but is not a member of %s",
+                                name, value, Arrays.toString(matches));
+                }
+            }
+            pw.append(" ");
+            pw.append(name);
+            pw.append("='");
+            pw.append(v);
+            pw.append("'");
+        }
+    }
+
+    private String getNamespace() {
+        if (version != null) {
+            try {
+                return ServiceComponent.NAMESPACE_STEM + "/v" + version;
+            } catch (Exception e) {
+                error("version: specified on component header but not a valid version: "
+                        + version);
+                return null;
+            }
+        }
+        if (modified != null || configurationPolicy != null) {
+            version = new Version("1.1.0");
+            return ServiceComponent.NAMESPACE_STEM + "/v1.1.0";
+        }
+        return null;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/Make.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/Make.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/Make.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,101 @@
+package aQute.bnd.make;
+
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class Make {
+    Builder                               builder;
+    Map<Instruction, Map<String, String>> make;
+
+    public Make(Builder builder) {
+        this.builder = builder;
+        // builder.getPlugins().add(new MakeBnd());
+        // builder.getPlugins().add(new MakeCopy());
+    }
+
+    public Resource process(String source) {
+        Map<Instruction, Map<String, String>> make = getMakeHeader();
+        builder.trace("make " + source);
+
+        for (Map.Entry<Instruction, Map<String, String>> entry : make
+                .entrySet()) {
+            Instruction instr = (Instruction) entry.getKey();
+            Matcher m = instr.getMatcher(source);
+            if (m.matches() || instr.isNegated()) {
+                Map<String, String> arguments = replace(m, entry.getValue());
+                List<MakePlugin> plugins = builder.getPlugins(MakePlugin.class);
+                for (MakePlugin plugin : plugins) {
+                    try {
+                        Resource resource = plugin.make(builder,
+                                source, arguments);
+                        if (resource != null) {
+                            builder.trace("Made " + source + " from args "
+                                    + arguments + " with " + plugin);
+                            return resource;
+                        }
+                    } catch (Exception e) {
+                        builder.error("Plugin " + plugin
+                                + " generates error when use in making "
+                                + source + " with args " + arguments, e);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private Map<String, String> replace(Matcher m, Map<String, String> value) {
+        Map<String, String> newArgs = Processor.newMap();
+        for (Map.Entry<String, String> entry : value.entrySet()) {
+            String s = entry.getValue();
+            s = replace(m, s);
+            newArgs.put(entry.getKey(), s);
+        }
+        return newArgs;
+    }
+
+    String replace(Matcher m, CharSequence s) {
+        StringBuffer sb = new StringBuffer();
+        int max = '0' + m.groupCount() + 1;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '$' && i < s.length() - 1) {
+                c = s.charAt(++i);
+                if (c >= '0' && c <= max) {
+                    int index = c - '0';
+                    String replacement = m.group(index);
+                    if (replacement != null)
+                        sb.append(replacement);
+                } else {
+                    if (c == '$')
+                        i++;
+                    sb.append(c);
+                }
+            } else
+                sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    Map<Instruction, Map<String, String>> getMakeHeader() {
+        if (make != null)
+            return make;
+        make = Processor.newMap();
+
+        String s = builder.getProperty(Builder.MAKE);
+        Map<String, Map<String, String>> make = builder.parseHeader(s);
+        for (Iterator<Map.Entry<String, Map<String, String>>> e = make
+                .entrySet().iterator(); e.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = e.next();
+            String pattern = Processor.removeDuplicateMarker(entry.getKey());
+            
+            Instruction instr = Instruction.getPattern(pattern);
+            this.make.put(instr, entry.getValue());
+        }
+
+        return this.make;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeBnd.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeBnd.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeBnd.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,64 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class MakeBnd implements MakePlugin, Constants {
+    final static Pattern JARFILE = Pattern.compile("(.+)\\.(jar|ipa)");
+
+    public Resource make(Builder builder, String destination,
+            Map<String, String> argumentsOnMake) throws Exception {
+        String type = (String) argumentsOnMake.get("type");
+        if (!"bnd".equals(type))
+            return null;
+
+        String recipe = (String) argumentsOnMake.get("recipe");
+        if (recipe == null) {
+            builder.error("No recipe specified on a make instruction for "
+                    + destination);
+            return null;
+        }
+        File bndfile = builder.getFile(recipe);
+        if (bndfile.isFile()) {
+            // We do not use a parent because then we would
+            // build ourselves again. So we can not blindly
+            // inherit the properties.
+            Builder bchild = builder.getSubBuilder();
+            bchild.removeBundleSpecificHeaders();
+            
+            // We must make sure that we do not include ourselves again!
+            bchild.setProperty(Analyzer.INCLUDE_RESOURCE, "");
+            bchild.setProperties(bndfile, builder.getBase());
+            
+            Jar jar = bchild.build();
+            Jar dot = builder.getTarget();
+
+            if (builder.hasSources()) {
+                for (String key : jar.getResources().keySet()) {
+                    if (key.startsWith("OSGI-OPT/src"))
+                        dot.putResource(key, (Resource) jar.getResource(key));
+                }
+            }
+            builder.getInfo(bchild, bndfile.getName() +": ");
+            String debug = bchild.getProperty(DEBUG);
+            if (Processor.isTrue(debug)) {
+                if ( builder instanceof ProjectBuilder ) {
+                    ProjectBuilder pb = (ProjectBuilder) builder;
+                    File target = pb.getProject().getTarget();
+                    target.mkdirs();
+                    String bsn = bchild.getBsn();
+                    File output = new File(target, bsn+".jar");
+                    jar.write(output);
+                }
+            }
+            return new JarResource(jar);
+        } else
+            return null;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeCopy.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeCopy.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/MakeCopy.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,45 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class MakeCopy implements MakePlugin {
+
+    public Resource make(Builder builder, String destination,
+            Map<String, String> argumentsOnMake) throws Exception {
+        String type = argumentsOnMake.get("type");
+        if (!type.equals("copy"))
+            return null;
+
+        String from = argumentsOnMake.get("from");
+        if (from == null) {
+            String content = argumentsOnMake.get("content");
+            if (content == null)
+                throw new IllegalArgumentException(
+                        "No 'from' or 'content' field in copy "
+                                + argumentsOnMake);
+            return new EmbeddedResource(content.getBytes("UTF-8"),0);
+        } else {
+
+            File f = builder.getFile(from);
+            if (f.isFile())
+                return new FileResource(f);
+            else {
+                try {
+                    URL url = new URL(from);
+                    return new URLResource(url);
+                } catch(MalformedURLException mfue) {
+                    // We ignore this
+                }
+                throw new IllegalArgumentException(
+                        "Copy source does not exist " + from
+                                + " for destination " + destination);
+            }
+        }
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ServiceComponent.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ServiceComponent.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/make/ServiceComponent.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,413 @@
+package aQute.bnd.make;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.filter.*;
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
+
+/**
+ * This class is an analyzer plugin. It looks at the properties and tries to
+ * find out if the Service-Component header contains the bnd shortut syntax. If
+ * not, the header is copied to the output, if it does, an XML file is created
+ * and added to the JAR and the header is modified appropriately.
+ */
+public class ServiceComponent implements AnalyzerPlugin {
+    public final static String      NAMESPACE_STEM                 = "http://www.osgi.org/xmlns/scr";
+    public final static String      JIDENTIFIER                    = "<<identifier>>";
+    public final static String      COMPONENT_FACTORY              = "factory:";
+    public final static String      COMPONENT_SERVICEFACTORY       = "servicefactory:";
+    public final static String      COMPONENT_IMMEDIATE            = "immediate:";
+    public final static String      COMPONENT_ENABLED              = "enabled:";
+    public final static String      COMPONENT_DYNAMIC              = "dynamic:";
+    public final static String      COMPONENT_MULTIPLE             = "multiple:";
+    public final static String      COMPONENT_PROVIDE              = "provide:";
+    public final static String      COMPONENT_OPTIONAL             = "optional:";
+    public final static String      COMPONENT_PROPERTIES           = "properties:";
+    public final static String      COMPONENT_IMPLEMENTATION       = "implementation:";
+
+    // v1.1.0
+    public final static String      COMPONENT_VERSION              = "version:";
+    public final static String      COMPONENT_CONFIGURATION_POLICY = "configuration-policy:";
+    public final static String      COMPONENT_MODIFIED             = "modified:";
+    public final static String      COMPONENT_ACTIVATE             = "activate:";
+    public final static String      COMPONENT_DEACTIVATE           = "deactivate:";
+
+    public final static String[]    componentDirectives            = new String[] {
+            COMPONENT_FACTORY, COMPONENT_IMMEDIATE, COMPONENT_ENABLED,
+            COMPONENT_DYNAMIC, COMPONENT_MULTIPLE, COMPONENT_PROVIDE,
+            COMPONENT_OPTIONAL, COMPONENT_PROPERTIES, COMPONENT_IMPLEMENTATION,
+            COMPONENT_SERVICEFACTORY, COMPONENT_VERSION,
+            COMPONENT_CONFIGURATION_POLICY, COMPONENT_MODIFIED,
+            COMPONENT_ACTIVATE, COMPONENT_DEACTIVATE              };
+
+    public final static Set<String> SET_COMPONENT_DIRECTIVES       = new HashSet<String>(
+                                                                           Arrays
+                                                                                   .asList(componentDirectives));
+
+    public final static Set<String> SET_COMPONENT_DIRECTIVES_1_1   = //
+                                                                   new HashSet<String>(
+                                                                           Arrays
+                                                                                   .asList(
+                                                                                           COMPONENT_VERSION,
+                                                                                           COMPONENT_CONFIGURATION_POLICY,
+                                                                                           COMPONENT_MODIFIED,
+                                                                                           COMPONENT_ACTIVATE,
+                                                                                           COMPONENT_DEACTIVATE));
+
+    public boolean analyzeJar(Analyzer analyzer) throws Exception {
+
+        ComponentMaker m = new ComponentMaker(analyzer);
+
+        Map<String, Map<String, String>> l = m.doServiceComponent();
+
+        if (!l.isEmpty())
+            analyzer.setProperty(Constants.SERVICE_COMPONENT, Processor
+                    .printClauses(l, ""));
+
+        analyzer.getInfo(m, "Service Component");
+        m.close();
+        return false;
+    }
+
+    private static class ComponentMaker extends Processor {
+        Analyzer analyzer;
+
+        ComponentMaker(Analyzer analyzer) {
+            super(analyzer);
+            this.analyzer = analyzer;
+        }
+
+        Map<String, Map<String, String>> doServiceComponent() throws Exception {
+            String header = getProperty(SERVICE_COMPONENT);
+            return doServiceComponent(header);
+        }
+
+        /**
+         * Check if a service component header is actually referring to a class.
+         * If so, replace the reference with an XML file reference. This makes
+         * it easier to create and use components.
+         * 
+         * @throws UnsupportedEncodingException
+         * 
+         */
+        public Map<String, Map<String, String>> doServiceComponent(
+                String serviceComponent) throws IOException {
+            Map<String, Map<String, String>> list = newMap();
+            Map<String, Map<String, String>> sc = parseHeader(serviceComponent);
+            Map<String, String> empty = Collections.emptyMap();
+
+            for (Map.Entry<String, Map<String, String>> entry : sc.entrySet()) {
+                String name = entry.getKey();
+                Map<String, String> info = entry.getValue();
+                if (name == null) {
+                    error("No name in Service-Component header: " + info);
+                    continue;
+                }
+                if (name.indexOf("*") >= 0 || name.indexOf('/') >= 0
+                        || name.endsWith(".xml")
+                        || analyzer.getJar().exists(name)) {
+                    // Normal service component, we do not process them
+                    list.put(name, info);
+                } else {
+                    String impl = name;
+
+                    if (info.containsKey(COMPONENT_IMPLEMENTATION))
+                        impl = info.get(COMPONENT_IMPLEMENTATION);
+
+                    if (!analyzer.checkClass(impl)) {
+                        error("Not found Service-Component header: " + name);
+                    } else {
+                        // We have a definition, so make an XML resources
+                        Resource resource = createComponentResource(name, info);
+                        analyzer.getJar().putResource(
+                                "OSGI-INF/" + name + ".xml", resource);
+                        list.put("OSGI-INF/" + name + ".xml", empty);
+                    }
+                }
+            }
+            return list;
+        }
+
+        /**
+         * Create the resource for a DS component.
+         * 
+         * @param list
+         * @param name
+         * @param info
+         * @throws UnsupportedEncodingException
+         */
+        Resource createComponentResource(String name, Map<String, String> info)
+                throws IOException {
+            String namespace = getNamespace(info);
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            PrintWriter pw = new PrintWriter(new OutputStreamWriter(out,
+                    "UTF-8"));
+            pw.println("<?xml version='1.0' encoding='utf-8'?>");
+            pw.print("<component name='" + name + "'");
+            if (namespace != null) {
+                pw.print(" xmlns='" + namespace + "'");
+            }
+
+            doAttribute(pw, info.get(COMPONENT_FACTORY), "factory");
+            doAttribute(pw, info.get(COMPONENT_IMMEDIATE), "immediate",
+                    "false", "true");
+            doAttribute(pw, info.get(COMPONENT_ENABLED), "enabled", "true",
+                    "false");
+            doAttribute(pw, info.get(COMPONENT_CONFIGURATION_POLICY),
+                    "configuration-policy", "optional", "require", "ignore");
+            doAttribute(pw, info.get(COMPONENT_ACTIVATE), "activate",
+                    JIDENTIFIER);
+            doAttribute(pw, info.get(COMPONENT_DEACTIVATE), "deactivate",
+                    JIDENTIFIER);
+            doAttribute(pw, info.get(COMPONENT_MODIFIED), "modified",
+                    JIDENTIFIER);
+
+            pw.println(">");
+
+            // Allow override of the implementation when people
+            // want to choose their own name
+            String impl = (String) info.get(COMPONENT_IMPLEMENTATION);
+            pw.println("  <implementation class='"
+                    + (impl == null ? name : impl) + "'/>");
+
+            String provides = info.get(COMPONENT_PROVIDE);
+            boolean servicefactory = Boolean.getBoolean(info
+                    .get(COMPONENT_SERVICEFACTORY)
+                    + "");
+            provides(pw, provides, servicefactory);
+            properties(pw, info);
+            reference(info, pw);
+            pw.println("</component>");
+            pw.close();
+            byte[] data = out.toByteArray();
+            out.close();
+            return new EmbeddedResource(data, 0);
+        }
+
+        private void doAttribute(PrintWriter pw, String value, String name,
+                String... matches) {
+            if (value != null) {
+                if (matches.length != 0) {
+                    if (matches.length == 1 && matches[0].equals(JIDENTIFIER)) {
+                        if (!Verifier.isIdentifier(value))
+                            error(
+                                    "Component attribute %s has value %s but is not a Java identifier",
+                                    name, value);
+                    } else {
+
+                        if (!Verifier.isMember(value, matches))
+                            error(
+                                    "Component attribute %s has value %s but is not a member of %s",
+                                    name, value, Arrays.toString(matches));
+                    }
+                }
+                pw.print(" ");
+                pw.print(name);
+                pw.print("='");
+                pw.print(value);
+                pw.print("'");
+            }
+        }
+
+        /**
+         * Check if we need to use the v1.1 namespace (or later).
+         * 
+         * @param info
+         * @return
+         */
+        private String getNamespace(Map<String, String> info) {
+            String version = info.get(COMPONENT_VERSION);
+            if (version != null) {
+                try {
+                    Version v = new Version(version);
+                    return NAMESPACE_STEM + "/v" + v;
+                } catch (Exception e) {
+                    error("version: specified on component header but not a valid version: "
+                            + version);
+                    return null;
+                }
+            }
+            for (String key : info.keySet()) {
+                if (SET_COMPONENT_DIRECTIVES_1_1.contains(key)) {
+                    return NAMESPACE_STEM + "/v1.1.0";
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Print the Service-Component properties element
+         * 
+         * @param pw
+         * @param info
+         */
+        void properties(PrintWriter pw, Map<String, String> info) {
+            Collection<String> properties = split(info
+                    .get(COMPONENT_PROPERTIES));
+            for (Iterator<String> p = properties.iterator(); p.hasNext();) {
+                String clause = p.next();
+                int n = clause.indexOf('=');
+                if (n <= 0) {
+                    error("Not a valid property in service component: "
+                            + clause);
+                } else {
+                    String type = null;
+                    String name = clause.substring(0, n);
+                    if (name.indexOf('@') >= 0) {
+                        String parts[] = name.split("@");
+                        name = parts[1];
+                        type = parts[0];
+                    }
+                    String value = clause.substring(n + 1).trim();
+                    // TODO verify validity of name and value.
+                    pw.print("<property name='");
+                    pw.print(name);
+                    pw.print("'");
+
+                    if (type != null) {
+                        if (VALID_PROPERTY_TYPES.matcher(type).matches()) {
+                            pw.print(" type='");
+                            pw.print(type);
+                            pw.print("'");
+                        } else {
+                            warning("Invalid property type '" + type
+                                    + "' for property " + name);
+                        }
+                    }
+
+                    String parts[] = value.split("\\s*(\\||\\n)\\s*");
+                    if (parts.length > 1) {
+                        pw.println(">");
+                        for (String part : parts) {
+                            pw.println(part);
+                        }
+                        pw.println("</property>");
+                    } else {
+                        pw.print(" value='");
+                        pw.print(parts[0]);
+                        pw.print("'/>");
+                    }
+                }
+            }
+        }
+
+        /**
+         * @param pw
+         * @param provides
+         */
+        void provides(PrintWriter pw, String provides, boolean servicefactory) {
+            if (provides != null) {
+                if (!servicefactory)
+                    pw.println("  <service>");
+                else
+                    pw.println("  <service servicefactory='true'>");
+
+                StringTokenizer st = new StringTokenizer(provides, ",");
+                while (st.hasMoreTokens()) {
+                    String interfaceName = st.nextToken();
+                    pw.println("    <provide interface='" + interfaceName
+                            + "'/>");
+                    if (!analyzer.checkClass(interfaceName))
+                        error("Component definition provides a class that is neither imported nor contained: "
+                                + interfaceName);
+                }
+                pw.println("  </service>");
+            }
+        }
+
+        public final static Pattern REFERENCE = Pattern
+                                                      .compile("([^(]+)(\\(.+\\))?");
+
+        /**
+         * @param info
+         * @param pw
+         */
+
+        void reference(Map<String, String> info, PrintWriter pw) {
+            Collection<String> dynamic = new ArrayList<String>(split(info
+                    .get(COMPONENT_DYNAMIC)));
+            Collection<String> optional = new ArrayList<String>(split(info
+                    .get(COMPONENT_OPTIONAL)));
+            Collection<String> multiple = new ArrayList<String>(split(info
+                    .get(COMPONENT_MULTIPLE)));
+
+            for (Iterator<Map.Entry<String, String>> r = info.entrySet()
+                    .iterator(); r.hasNext();) {
+                Map.Entry<String, String> ref = r.next();
+                String referenceName = (String) ref.getKey();
+                String target = null;
+                String interfaceName = (String) ref.getValue();
+                if (interfaceName == null || interfaceName.length() == 0) {
+                    error("Invalid Interface Name for references in Service Component: "
+                            + referenceName + "=" + interfaceName);
+                }
+                char c = interfaceName.charAt(interfaceName.length() - 1);
+                if ("?+*~".indexOf(c) >= 0) {
+                    if (c == '?' || c == '*' || c == '~')
+                        optional.add(referenceName);
+                    if (c == '+' || c == '*')
+                        multiple.add(referenceName);
+                    if (c == '+' || c == '*' || c == '?')
+                        dynamic.add(referenceName);
+                    interfaceName = interfaceName.substring(0, interfaceName
+                            .length() - 1);
+                }
+
+                if (referenceName.endsWith(":")) {
+                    if (!SET_COMPONENT_DIRECTIVES.contains(referenceName))
+                        error("Unrecognized directive in Service-Component header: "
+                                + referenceName);
+                    continue;
+                }
+
+                Matcher m = REFERENCE.matcher(interfaceName);
+                if (m.matches()) {
+                    interfaceName = m.group(1);
+                    target = m.group(2);
+                }
+
+                if (!analyzer.checkClass(interfaceName))
+                    error("Component definition refers to a class that is neither imported nor contained: "
+                            + interfaceName);
+
+                pw.print("  <reference name='" + referenceName
+                        + "' interface='" + interfaceName + "'");
+
+                String cardinality = optional.contains(referenceName) ? "0"
+                        : "1";
+                cardinality += "..";
+                cardinality += multiple.contains(referenceName) ? "n" : "1";
+                if (!cardinality.equals("1..1"))
+                    pw.print(" cardinality='" + cardinality + "'");
+
+                if (Character.isLowerCase(referenceName.charAt(0))) {
+                    String z = referenceName.substring(0, 1).toUpperCase()
+                            + referenceName.substring(1);
+                    pw.print(" bind='set" + z + "'");
+                    pw.print(" unbind='unset" + z + "'");
+                    // TODO Verify that the methods exist
+                }
+
+                if (dynamic.contains(referenceName)) {
+                    pw.print(" policy='dynamic'");
+                }
+
+                if (target != null) {
+                    //Filter filter = new Filter(target);
+                    //if (filter.verify() == null)
+                    //    pw.print(" target='" + filter.toString() + "'");
+                    //else
+                    //    error("Target for " + referenceName
+                    //            + " is not a correct filter: " + target + " "
+                    //            + filter.verify());
+                    pw.print(" target='" + target + "'");
+                }
+                pw.println("/>");
+            }
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/BsnToMavenPath.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/BsnToMavenPath.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/BsnToMavenPath.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,5 @@
+package aQute.bnd.maven;
+
+public interface BsnToMavenPath {
+    String[] getGroupAndArtifact(String bsn);
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenGroup.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenGroup.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenGroup.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,27 @@
+package aQute.bnd.maven;
+
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.libg.reporter.*;
+
+public class MavenGroup implements BsnToMavenPath, Plugin {
+    String    groupId = "";
+
+    public String[] getGroupAndArtifact(String bsn) {
+        String[] result = new String[2];
+        result[0] = groupId;
+        result[1] = bsn;
+        return result;
+    }
+
+    public void setProperties(Map<String, String> map) {
+        if (map.containsKey("groupId")) {
+            groupId = map.get("groupId");
+        }
+    }
+
+    public void setReporter(Reporter processor) {
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenRepository.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenRepository.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/maven/MavenRepository.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,191 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class MavenRepository implements RepositoryPlugin, Plugin,
+        BsnToMavenPath {
+
+	public static String NAME    = "name";
+	
+    File     root;
+    Reporter reporter;
+    String name;
+    
+    public String toString() {
+        return "maven:" + root;
+    }
+
+    public boolean canWrite() {
+        return false;
+    }
+
+    public File[] get(String bsn, String version) throws Exception {
+        VersionRange range = new VersionRange("0");
+        if (version != null)
+            range = new VersionRange(version);
+
+        List<BsnToMavenPath> plugins = ((Processor) reporter)
+                .getPlugins(BsnToMavenPath.class);
+
+        for (BsnToMavenPath cvr : plugins) {
+            String[] paths = cvr.getGroupAndArtifact(bsn);
+            if (paths != null) {
+                File[] files = find(paths[0], paths[1], range);
+                if (files != null)
+                    return files;
+            }
+        }
+        reporter.trace("Cannot find in maven: %s-%s", bsn, version);
+        return null;
+    }
+
+    File[] find(String groupId, String artifactId, VersionRange range) {
+        String path = groupId.replace(".", "/");
+        File vsdir = Processor.getFile(root, path);
+        if (!vsdir.isDirectory())
+            return null;
+
+        vsdir = Processor.getFile(vsdir, artifactId);
+
+        List<File> result = new ArrayList<File>();
+        if (vsdir.isDirectory()) {
+            String versions[] = vsdir.list();
+            for (String v : versions) {
+                String vv = Analyzer.cleanupVersion(v);
+                if (Verifier.isVersion(vv)) {
+                    Version vvv = new Version(vv);
+                    if (range.includes(vvv)) {
+                        File file = Processor.getFile(vsdir, v + "/"
+                                + artifactId + "-" + v + ".jar");
+                        if (file.isFile())
+                            result.add(file);
+                        else
+                            reporter
+                                    .warning(
+                                            "Expected maven entry was not a valid file %s ",
+                                            file);
+                    }
+                } else {
+                    reporter
+                            .warning(
+                                    "Expected a version directory in maven: dir=%s raw-version=%s cleaned-up-version=%s",
+                                    vsdir, vv, v);
+                }
+            }
+        } else
+            return null;
+
+        return result.toArray(new File[result.size()]);
+    }
+
+    public List<String> list(String regex) {
+        List<String> bsns = new ArrayList<String>();
+        Pattern match = Pattern.compile(".*");
+        if (regex != null)
+            match = Pattern.compile(regex);
+        find(bsns, match, root, "");
+        return bsns;
+    }
+
+    void find(List<String> bsns, Pattern pattern, File base, String name) {
+        if (base.isDirectory()) {
+            String list[] = base.list();
+            boolean found = false;
+            for (String entry : list) {
+                char c = entry.charAt(0);
+                if (c >= '0' && c <= '9') {
+                    if (pattern.matcher(name).matches())
+                        found = true;
+                } else {
+                    String nextName = entry;
+                    if (name.length() != 0)
+                        nextName = name + "." + entry;
+
+                    File next = Processor.getFile(base, entry);
+                    find(bsns, pattern, next, nextName);
+                }
+            }
+            if (found)
+                bsns.add(name);
+        }
+    }
+
+    public File put(Jar jar) throws Exception {
+        throw new IllegalStateException(
+                "Maven does not support the put command");
+    }
+
+    public List<Version> versions(String bsn) {
+        String path = bsn.replace('.', '/');
+        File base = Processor.getFile(root, path);
+        if (!base.isDirectory()) {
+            reporter.warning("Expected a directory %s", base);
+            return null;
+        }
+
+        List<Version> result = new ArrayList<Version>();
+
+        String[] versions = base.list();
+        for (String v : versions) {
+            v = Analyzer.cleanupVersion(v);
+            if (Verifier.VERSION.matcher(v).matches()) {
+                result.add(new Version(v));
+            } else {
+                if (reporter.isPedantic()) {
+                    reporter
+                            .warning(
+                                    "Invalid version in maven base directory: %s",
+                                    base);
+                }
+            }
+        }
+        return result;
+    }
+
+    public void setProperties(Map<String, String> map) {
+        String root = map.get("root");
+        if (root == null) {
+            String home = System.getProperty("user.home");
+            root = home + "/.m2/repository";
+        }
+        this.root = Processor.getFile(new File(""), root).getAbsoluteFile();
+        if (!this.root.isDirectory()) {
+            reporter
+                    .error(
+                            "Maven repository did not get a proper URL to the repository %s",
+                            root);
+        }
+        name = (String) map.get(NAME);
+        
+    }
+
+    public void setReporter(Reporter processor) {
+        this.reporter = processor;
+    }
+
+    public String[] getGroupAndArtifact(String bsn) {
+        int n = bsn.lastIndexOf('.');
+        if (n < 0) {
+            return new String[] { bsn, bsn };
+        }
+        String groupId = bsn.substring(0, n);
+        String artifactId = bsn.substring(n + 1);
+
+        return new String[] { groupId, artifactId };
+    }
+
+	public String getName() {
+		if (name == null) {
+			return toString();
+		}
+		return name;
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/ActionWrapper.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/ActionWrapper.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/ActionWrapper.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,29 @@
+package aQute.bnd.plugin;
+
+import org.osgi.framework.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.service.action.*;
+
+public class ActionWrapper implements Action {
+    ServiceReference ref;
+    BundleContext context;
+    
+    public ActionWrapper(BundleContext context, ServiceReference ref) {
+        this.ref = ref;
+        this.context = context;
+    }
+
+    public void execute(Project project, String action) throws Exception {
+        Action a = (Action) context.getService(ref);
+        if ( a == null )
+            throw new IllegalStateException("Command provider is gone");
+        
+        try {
+            a.execute(project, action);
+        } finally {
+            context.ungetService(ref);
+        }
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Activator.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Activator.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Activator.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,225 @@
+package aQute.bnd.plugin;
+
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.resource.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.ui.plugin.*;
+import org.osgi.framework.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.service.action.*;
+import aQute.lib.osgi.*;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+    // The plug-in ID
+    public static final String PLUGIN_ID = "aQute.bmaker";
+
+    // The shared instance
+    private static Activator   plugin;
+    BundleContext              context;
+    Central central;
+    
+    /**
+     * The constructor
+     */
+    public Activator() {
+        plugin = this;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+     */
+    public void start(BundleContext context) throws Exception {
+        super.start(context);
+        plugin = this;
+        this.context = context;
+        this.central = new Central(context);
+        
+        Hashtable<String,Object> p = new Hashtable<String, Object>();
+        // p.put(Action.ACTION_MENU, new String[] {"a:b", "a:c", "a:d", "a:d:e"});
+        context.registerService(Action.class.getName(), new ReflectAction(""), p);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+     */
+    public void stop(BundleContext context) throws Exception {
+        plugin = null;
+        super.stop(context);
+        central.close();
+    }
+
+    /**
+     * Returns the shared instance
+     * 
+     * @return the shared instance
+     */
+    public static Activator getDefault() {
+        return plugin;
+    }
+
+    /**
+     * Returns an image descriptor for the image file at the given plug-in
+     * relative path
+     * 
+     * @param path
+     *            the path
+     * @return the image descriptor
+     */
+    public static ImageDescriptor getImageDescriptor(String path) {
+        return imageDescriptorFromPlugin(PLUGIN_ID, path);
+    }
+
+    static volatile boolean busy;
+    public void error(final String msg, final Throwable t) {
+        Status s = new Status(Status.ERROR, PLUGIN_ID, 0, msg, t);
+        getLog().log(s);
+        async(new Runnable() {
+            public void run() {
+                synchronized(this) {
+                    if  ( busy )
+                        return;
+                    busy = true;
+                }
+                Status s = new Status(Status.ERROR, PLUGIN_ID, 0, "", null);
+                ErrorDialog.openError(null, "Errors during bundle generation",
+                        msg + " " + t.getMessage(), s);
+                
+                busy = false;
+            }
+        });
+    }
+
+    public void info(String msg) {
+        Status s = new Status(Status.INFO, PLUGIN_ID, 0, msg, null);
+        getLog().log(s);
+    }
+
+    public void error(List<String> errors) {
+        final StringBuffer sb = new StringBuffer();
+        for (String msg : errors) {
+            sb.append(msg);
+            sb.append("\n");
+        }
+
+        async(new Runnable() {
+            public void run() {
+                Status s = new Status(Status.ERROR, PLUGIN_ID, 0, "", null);
+                ErrorDialog.openError(null, "Errors during bundle generation",
+                        sb.toString(), s);
+            }
+        });
+    }
+
+    public void message(final String msg) {
+        async(new Runnable() {
+            public void run() {
+                MessageDialog.openInformation(null, "Bnd", msg);
+            }
+        });
+    }
+
+    public void warning(List<String> errors) {
+        final StringBuffer sb = new StringBuffer();
+        for (String msg : errors) {
+            sb.append(msg);
+            sb.append("\n");
+        }
+        async(new Runnable() {
+            public void run() {
+                Status s = new Status(Status.WARNING, PLUGIN_ID, 0, "", null);
+                ErrorDialog.openError(null,
+                        "Warnings during bundle generation", sb.toString(), s);
+            }
+        });
+    }
+
+    void async(Runnable run) {
+        if (Display.getCurrent() == null) {
+            Display.getDefault().asyncExec(run);
+        } else
+            run.run();
+    }
+
+    public boolean getReportDone() {
+        return true;
+        // return
+        // getPreferenceStore().getBoolean(PreferenceConstants.P_REPORT_DONE);
+    }
+
+    public File getCopy() {
+        return null;
+
+        // String path =
+        // getPreferenceStore().getString(PreferenceConstants.P_COPY);
+        // if ( path == null )
+        // return null;
+
+        // File file = new File(path);
+        // if ( !file.exists() || file.isFile() )
+        // return null;
+
+        // return file;
+    }
+
+    public boolean isPedantic() {
+        return false;
+        // IPreferenceStore store = getPreferenceStore();
+        // return store.getBoolean(PreferenceConstants.P_PEDANTIC);
+    }
+
+    public BundleContext getBundleContext() {
+        return context;
+    }
+
+    public void report(boolean warnings, boolean acknowledge , Processor reporter, final String title, final String extra ) {
+        if (reporter.getErrors().size() > 0
+                || (warnings && reporter.getWarnings().size() > 0)) {
+            final StringBuffer sb = new StringBuffer();
+            sb.append("\n");
+            if (reporter.getErrors().size() > 0) {
+                sb.append("[Errors]\n");
+                for (String msg : reporter.getErrors()) {
+                    sb.append(msg);
+                    sb.append("\n");
+                }
+            }
+            sb.append("\n");
+            if (reporter.getWarnings().size() > 0) {
+                sb.append("[Warnings]\n");
+                for (String msg : reporter.getWarnings()) {
+                    sb.append(msg);
+                    sb.append("\n");
+                }
+            }
+            final Status s = new Status(Status.ERROR, PLUGIN_ID, 0, sb.toString(), null);
+            reporter.clear();
+            
+            async(new Runnable() {
+                public void run() {
+                    ErrorDialog.openError(null, title, title + "\n" + extra, s);
+                }
+            });
+
+        } else {
+            message(title+ " : ok");
+        }
+    }
+
+    public Central getCentral() {
+        return central;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Central.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Central.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/Central.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,264 @@
+package aQute.bnd.plugin;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jdt.core.*;
+import org.osgi.framework.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.classpath.*;
+import aQute.bnd.service.*;
+
+ at SuppressWarnings("unchecked")
+public class Central implements IResourceChangeListener {
+    static IWorkspace                iworkspace;
+    final Map<IJavaProject, Project> javaProjectToModel = new HashMap<IJavaProject, Project>();
+    final List<ModelListener>        listeners          = new CopyOnWriteArrayList<ModelListener>();
+    static Workspace                 workspace;
+    final BundleContext              context;
+    final Workspace                  ws;
+
+    Central(BundleContext context) throws Exception {
+        this.context = context;
+        // Add a resource change listener if this is
+        // the first project
+        iworkspace = ResourcesPlugin.getWorkspace();
+        iworkspace.addResourceChangeListener(this);
+
+        ws = getWorkspace();
+        context.registerService(Workspace.class.getName(), ws, null);
+    }
+
+    public Project getModel(IJavaProject project) {
+        try {
+            Project model = javaProjectToModel.get(project);
+            if (model == null) {
+                File projectDir = project.getProject().getLocation()
+                        .makeAbsolute().toFile();
+                model = Workspace.getProject(projectDir);
+                if (workspace == null) {
+                    model.getWorkspace();
+                }
+                if (model != null) {
+                    javaProjectToModel.put(project, model);
+                }
+            }
+            return model;
+        } catch (Exception e) {
+            // TODO do something more useful here
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Implementation of the resource changed interface. We are checking in the
+     * POST_CHANGE phase if one of our tracked models needs to be updated.
+     */
+    public synchronized void resourceChanged(IResourceChangeEvent event) {
+        if (event.getType() != IResourceChangeEvent.POST_CHANGE)
+            return;
+
+        IResourceDelta rootDelta = event.getDelta();
+        try {
+            final Set<Project> changed = new HashSet<Project>();
+            rootDelta.accept(new IResourceDeltaVisitor() {
+                public boolean visit(IResourceDelta delta) throws CoreException {
+                    try {
+
+                        IPath location = delta.getResource().getLocation();
+                        if (location == null) {
+                            System.out
+                                    .println("Cannot convert resource to file: "
+                                            + delta.getResource());
+                        } else {
+                            File file = location.toFile();
+                            File parent = file.getParentFile();
+                            boolean parentIsWorkspace = parent
+                                    .equals(getWorkspace().getBase());
+
+                            // file
+                            // /development/osgi/svn/build/org.osgi.test.cases.distribution/bnd.bnd
+                            // parent
+                            // /development/osgi/svn/build/org.osgi.test.cases.distribution
+                            // workspace /development/amf/workspaces/osgi
+                            // false
+
+                            if (parent != null && parentIsWorkspace) {
+                                // We now are on project level, we do not go
+                                // deeper
+                                // because projects/workspaces should check for
+                                // any
+                                // changes.
+                                // We are careful not to create unnecessary
+                                // projects
+                                // here.
+                                if (file.getName().equals(Workspace.CNFDIR)) {
+                                    if (workspace.refresh()) {
+                                        changed.addAll(workspace
+                                                .getCurrentProjects());
+                                    }
+                                    return false;
+                                }
+                                if (workspace.isPresent(file.getName())) {
+                                    Project project = workspace.getProject(file
+                                            .getName());
+                                    changed.add(project);
+                                } else {
+                                    ; // Project not created yet, so we
+                                    // have
+                                    // no cached results
+
+                                }
+                                return false;
+                            }
+                        }
+                        return true;
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        throw new CoreException(new Status(Status.ERROR,
+                                Activator.PLUGIN_ID,
+                                "During checking project changes", e));
+                    }
+                }
+
+            });
+
+            for (Project p : changed) {
+                p.refresh();
+                changed(p);
+
+            }
+        } catch (CoreException e) {
+            Activator.getDefault().error("While handling changes", e);
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    public static Workspace getWorkspace() throws Exception {
+        if (workspace != null)
+            return workspace;
+
+        IResource resource = iworkspace.getRoot().findMember("/cnf/build.bnd");
+        if (resource != null) {
+            IPath path = resource.getLocation();
+            if (path != null) {
+                File f = path.toFile();
+                workspace = Workspace.getWorkspace(f.getAbsoluteFile()
+                        .getParentFile().getParentFile().getAbsoluteFile());
+                // workspace.setBundleContex(context);
+                return workspace;
+            }
+        }
+
+        workspace = Workspace.getWorkspace(iworkspace.getRoot().getLocation()
+                .toFile());
+        return workspace;
+    }
+
+    public void changed(Project model) {
+        model.setChanged();
+        for (ModelListener m : listeners)
+            try {
+                m.modelChanged(model);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+    }
+
+    public void addModelListener(ModelListener m) {
+        if (!listeners.contains(m)) {
+            listeners.add(m);
+        }
+    }
+
+    public void removeModelListener(ModelListener m) {
+        listeners.remove(m);
+    }
+
+    public IJavaProject getJavaProject(Project model) {
+        for (IProject iproj : iworkspace.getRoot().getProjects()) {
+            if (iproj.getName().equals(model.getName())) {
+                IJavaProject ij = JavaCore.create(iproj);
+                if (ij != null && ij.exists()) {
+                    return ij;
+                }
+                // current project is not a Java project
+            }
+        }
+        return null;
+    }
+
+    public static IPath toPath(Project project, File file) {
+        String path = file.getAbsolutePath();
+        String workspace = project.getWorkspace().getBase().getAbsolutePath();
+        if (path.startsWith(workspace))
+            path = path.substring(workspace.length());
+        else
+            return null;
+
+        IPath p = new Path(path);
+        return p;
+    }
+
+    public static void refresh(IPath path) {
+        try {
+            IResource r = ResourcesPlugin.getWorkspace().getRoot().findMember(
+                    path);
+            if (r != null)
+                return;
+
+            IPath p = (IPath) path.clone();
+            while (p.segmentCount() > 0) {
+                p = p.removeLastSegments(1);
+                IResource resource = ResourcesPlugin.getWorkspace().getRoot()
+                        .findMember(p);
+                if (resource != null) {
+                    resource.refreshLocal(2, null);
+                    return;
+                }
+            }
+        } catch (Exception e) {
+            Activator.getDefault().error("While refreshing path " + path, e);
+        }
+        System.out.println("Unexpectedly could not find path in workspace: "
+                + path);
+    }
+
+    public void refreshPlugins() throws Exception {
+        List<Refreshable> rps = getWorkspace().getPlugins(Refreshable.class);
+        for (Refreshable rp : rps) {
+            if (rp.refresh()) {
+                File dir = rp.getRoot();
+                refreshFile(dir);
+            }
+        }
+    }
+
+    public void refreshFile(File f) throws Exception {
+        String path = toLocal(f);
+        IResource r = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+        if (r != null) {
+            r.refreshLocal(IResource.DEPTH_INFINITE, null);
+        }
+    }
+
+    public void refresh(Project p) throws Exception {
+        IJavaProject jp = getJavaProject(p);
+        if (jp != null)
+            jp.getProject().refreshLocal(IResource.DEPTH_INFINITE, null);
+    }
+
+    private String toLocal(File f) throws Exception {
+        String root = getWorkspace().getBase().getAbsolutePath();
+        String path = f.getAbsolutePath().substring(root.length());
+        return path;
+    }
+
+    public void close() {
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndBuilder.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndBuilder.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndBuilder.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,135 @@
+package aQute.bnd.plugin.builder;
+
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+
+import aQute.bnd.plugin.*;
+import aQute.bnd.plugin.popup.actions.*;
+import aQute.lib.osgi.*;
+
+public class BndBuilder extends IncrementalProjectBuilder {
+
+	class DeltaVisitor implements IResourceDeltaVisitor {
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
+		 */
+		public boolean visit(IResourceDelta delta) throws CoreException {
+			IResource resource = delta.getResource();
+			switch (delta.getKind()) {
+			case IResourceDelta.ADDED:
+				// handle added resource
+				checkBnd(resource);
+				break;
+			case IResourceDelta.REMOVED:
+				// handle removed resource
+				break;
+			case IResourceDelta.CHANGED:
+				// handle changed resource
+				checkBnd(resource);
+				break;
+			}
+			// return true to continue visiting children.
+			return true;
+		}
+	}
+
+	class ResourceVisitor implements IResourceVisitor {
+		public boolean visit(IResource resource) {
+			checkBnd(resource);
+			// return true to continue visiting children.
+			return true;
+		}
+	}
+
+	public static final String	BUILDER_ID	= "biz.aQute.bnd.BndBuilder";
+
+	private static final String	MARKER_TYPE	= "biz.aQute.bnd.xmlProblem";
+
+	private void addMarker(IFile file, String message, int lineNumber,
+			int severity) {
+		try {
+			IMarker marker = file.createMarker(MARKER_TYPE);
+			marker.setAttribute(IMarker.MESSAGE, message);
+			marker.setAttribute(IMarker.SEVERITY, severity);
+			if (lineNumber == -1) {
+				lineNumber = 1;
+			}
+			marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
+		} catch (CoreException e) {
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
+	 *      java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+	 */
+	@SuppressWarnings("unchecked")
+    protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
+			throws CoreException {
+		if (kind == FULL_BUILD) {
+			fullBuild(monitor);
+		} else {
+			IResourceDelta delta = getDelta(getProject());
+			if (delta == null) {
+				fullBuild(monitor);
+			} else {
+				incrementalBuild(delta, monitor);
+			}
+		}
+		return null;
+	}
+
+	void checkBnd(IResource resource) {
+		if (resource instanceof IFile && resource.getName().endsWith(".bnd")) {
+			IFile file = (IFile) resource;
+			deleteMarkers(file);
+			try {
+				Builder builder = MakeBundle.setBuilder(Activator.getDefault(),
+						resource.getProject(), file.getLocation().toFile());
+				try {
+					builder.build();
+					builder.close();
+				} catch (Exception e1) {
+					addMarker(file, "Unexpected exception: " + e1, 1,
+							Status.ERROR);
+				}
+				for (Iterator<String> i = builder.getErrors().iterator(); i.hasNext();) {
+					addMarker(file, i.next(), 1, Status.ERROR);
+				}
+				for (Iterator<String> i = builder.getWarnings().iterator(); i.hasNext();) {
+					addMarker(file, i.next(), 1, Status.WARNING);
+				}
+			} catch (Exception e) {
+				addMarker(file, "Really bad exception: " + e, 1,
+						Status.ERROR);
+			}
+		}
+	}
+
+	private void deleteMarkers(IFile file) {
+		try {
+			file.deleteMarkers(MARKER_TYPE, false, IResource.DEPTH_ZERO);
+		} catch (CoreException ce) {
+		}
+	}
+
+	protected void fullBuild(final IProgressMonitor monitor)
+			throws CoreException {
+		try {
+			getProject().accept(new ResourceVisitor());
+		} catch (CoreException e) {
+		}
+	}
+
+	protected void incrementalBuild(IResourceDelta delta,
+			IProgressMonitor monitor) throws CoreException {
+		// the visitor does the work.
+		delta.accept(new DeltaVisitor());
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndNature.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndNature.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/BndNature.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,80 @@
+package aQute.bnd.plugin.builder;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+public class BndNature implements IProjectNature {
+
+	/**
+	 * ID of this project nature
+	 */
+	public static final String NATURE_ID = "biz.aQute.bnd.BndNature";
+
+	private IProject project;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.resources.IProjectNature#configure()
+	 */
+	public void configure() throws CoreException {
+		IProjectDescription desc = project.getDescription();
+		ICommand[] commands = desc.getBuildSpec();
+
+		for (int i = 0; i < commands.length; ++i) {
+			if (commands[i].getBuilderName().equals(BndBuilder.BUILDER_ID)) {
+				return;
+			}
+		}
+
+		ICommand[] newCommands = new ICommand[commands.length + 1];
+		System.arraycopy(commands, 0, newCommands, 0, commands.length);
+		ICommand command = desc.newCommand();
+		command.setBuilderName(BndBuilder.BUILDER_ID);
+		newCommands[newCommands.length - 1] = command;
+		desc.setBuildSpec(newCommands);
+		project.setDescription(desc, null);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.resources.IProjectNature#deconfigure()
+	 */
+	public void deconfigure() throws CoreException {
+		IProjectDescription description = getProject().getDescription();
+		ICommand[] commands = description.getBuildSpec();
+		for (int i = 0; i < commands.length; ++i) {
+			if (commands[i].getBuilderName().equals(BndBuilder.BUILDER_ID)) {
+				ICommand[] newCommands = new ICommand[commands.length - 1];
+				System.arraycopy(commands, 0, newCommands, 0, i);
+				System.arraycopy(commands, i + 1, newCommands, i,
+						commands.length - i - 1);
+				description.setBuildSpec(newCommands);
+				return;
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.resources.IProjectNature#getProject()
+	 */
+	public IProject getProject() {
+		return project;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject)
+	 */
+	public void setProject(IProject project) {
+		this.project = project;
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/ToggleNatureAction.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/ToggleNatureAction.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/builder/ToggleNatureAction.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,96 @@
+package aQute.bnd.plugin.builder;
+
+import java.util.Iterator;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class ToggleNatureAction implements IObjectActionDelegate {
+
+	private ISelection selection;
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+	 */
+	public void run(IAction action) {
+		if (selection instanceof IStructuredSelection) {
+			for (Iterator<?> it = ((IStructuredSelection) selection).iterator(); it
+					.hasNext();) {
+				Object element = it.next();
+				IProject project = null;
+				if (element instanceof IProject) {
+					project = (IProject) element;
+				} else if (element instanceof IAdaptable) {
+					project = (IProject) ((IAdaptable) element)
+							.getAdapter(IProject.class);
+				}
+				if (project != null) {
+					toggleNature(project);
+				}
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction,
+	 *      org.eclipse.jface.viewers.ISelection)
+	 */
+	public void selectionChanged(IAction action, ISelection selection) {
+		this.selection = selection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction,
+	 *      org.eclipse.ui.IWorkbenchPart)
+	 */
+	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+	}
+
+	/**
+	 * Toggles sample nature on a project
+	 * 
+	 * @param project
+	 *            to have sample nature added or removed
+	 */
+	private void toggleNature(IProject project) {
+		try {
+			IProjectDescription description = project.getDescription();
+			String[] natures = description.getNatureIds();
+
+			for (int i = 0; i < natures.length; ++i) {
+				if (BndNature.NATURE_ID.equals(natures[i])) {
+					// Remove the nature
+					String[] newNatures = new String[natures.length - 1];
+					System.arraycopy(natures, 0, newNatures, 0, i);
+					System.arraycopy(natures, i + 1, newNatures, i,
+							natures.length - i - 1);
+					description.setNatureIds(newNatures);
+					project.setDescription(description, null);
+					return;
+				}
+			}
+
+			// Add the nature
+			String[] newNatures = new String[natures.length + 1];
+			System.arraycopy(natures, 0, newNatures, 0, natures.length);
+			newNatures[natures.length] = BndNature.NATURE_ID;
+			description.setNatureIds(newNatures);
+			project.setDescription(description, null);
+		} catch (CoreException e) {
+		}
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndCompletionProcessor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndCompletionProcessor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndCompletionProcessor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,46 @@
+package aQute.bnd.plugin.editors;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.contentassist.*;
+
+import aQute.bnd.help.*;
+
+public class BndCompletionProcessor implements IContentAssistProcessor {
+
+    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
+            int offset) {
+        ICompletionProposal[] result= new ICompletionProposal[Syntax.HELP.size()];
+            int i=0;
+        for ( Syntax s : Syntax.HELP.values()) {
+            IContextInformation info= new ContextInformation(
+                    s.getHeader(), s.getHeader() +"-"+s.getLead());
+            result[i++]= new CompletionProposal(s.getHeader(), offset, 0, s.getHeader().length(), null, s.getLead(), info, s.getExample()); //$NON-NLS-1$
+        }
+        return result;
+    }
+
+    public IContextInformation[] computeContextInformation(ITextViewer viewer,
+            int offset) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public char[] getCompletionProposalAutoActivationCharacters() {
+        return new char[] {'-'};
+    }
+
+    public char[] getContextInformationAutoActivationCharacters() {
+        return new char[] {'-'};
+    }
+
+    public IContextInformationValidator getContextInformationValidator() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getErrorMessage() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndHover.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndHover.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndHover.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,117 @@
+package aQute.bnd.plugin.editors;
+
+import org.eclipse.jface.text.*;
+
+import aQute.bnd.help.*;
+
+public class BndHover implements ITextHover {
+    static class DocString implements CharSequence {
+        IDocument doc;
+
+        public char charAt(int index) {
+            try {
+                return doc.getChar(index);
+            } catch (BadLocationException e) {
+                throw new IndexOutOfBoundsException();
+            }
+        }
+
+        public int length() {
+            return doc.getLength();
+        }
+
+        public CharSequence subSequence(int start, int end) {
+            return doc.get().subSequence(start, end);
+        }
+
+    };
+
+    public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+        if (hoverRegion != null) {
+            IDocument doc = textViewer.getDocument();
+            try {
+                String key = doc.get(hoverRegion.getOffset(), hoverRegion
+                        .getLength());
+
+                Syntax syntax = Syntax.HELP.get(key);
+                if (syntax == null)
+                    return null;
+
+                StringBuilder sb = new StringBuilder();
+                sb.append(syntax.getLead());
+                sb.append("\nE.g. ");
+                sb.append(syntax.getExample());
+
+                String text = sb.toString();
+                if (text == null)
+                    return null;
+
+                if (text.length() > 30) {
+                    text = wrap(text, 30);
+                }
+                return text;
+            } catch (Exception e) {
+                return e + "";
+            }
+        }
+        return null;
+    }
+
+    public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+        IDocument doc = textViewer.getDocument();
+        try {
+            int start = offset;
+            int end = offset;
+            while (start >= 0 && isWordChar(doc.getChar(start)))
+                start--;
+
+            while (end < doc.getLength() && isWordChar(doc.getChar(end)))
+                end++;
+
+            start++;
+            int length = Math.min(doc.getLength(), end - start);
+            start = Math.max(0,start);
+            return new Region(start, length);
+        } catch (BadLocationException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    boolean isWordChar(char c) {
+        return Character.isJavaIdentifierPart(c) || c == '-' || c == '.';
+    }
+
+    String wrap(String text, int width) {
+        StringBuilder sb = new StringBuilder();
+        int n = 0;
+        int r = 0;
+        while (r < text.length()) {
+            char c = text.charAt(r++);
+            switch (c) {
+            case '\r':
+            case '\n':
+                if (n != 0)
+                    sb.append('\n');
+                n = 0;
+                break;
+            case ' ':
+            case '\t':
+                if (n > 20) {
+                    sb.append("\n");
+                    n = 0;
+                } else {
+                    sb.append(" ");
+                    n++;
+                }
+                break;
+            default:
+                sb.append(c);
+                n++;
+            }
+        }
+        return sb.toString();
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,248 @@
+package aQute.bnd.plugin.editors;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.*;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.*;
+import org.eclipse.ui.editors.text.*;
+import org.eclipse.ui.ide.*;
+import org.eclipse.ui.part.*;
+
+import aQute.bnd.plugin.*;
+
+/**
+ */
+public class BndMultiPageEditor extends MultiPageEditorPart implements
+        IResourceChangeListener {
+
+    private Table table;
+    /** The text editor used in page 0. */
+    private TextEditor editor;
+
+    /** The font chosen in page 1. */
+    private Font       font;
+
+    /** The text widget used in page 2. */
+    private StyledText text;
+
+    /**
+     * Creates a multi-page editor example.
+     */
+    public BndMultiPageEditor() {
+        super();
+        ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+    }
+
+    /**
+     * Creates page 0 of the multi-page editor, which contains a text editor.
+     */
+    void createTextEditor() {
+        try {
+            editor = new BndTextEditor();
+
+            int index = addPage(editor, getEditorInput());
+            setPageText(index, editor.getTitle());
+        } catch (PartInitException e) {
+            ErrorDialog.openError(getSite().getShell(),
+                    "Error creating nested text editor", null, e.getStatus());
+        }
+    }
+
+    /**
+     * Creates page 1 of the multi-page editor, which allows you to change the
+     * font used in page 2.
+     */
+    void createPage1() {
+
+        Composite composite = new Composite(getContainer(), SWT.NONE);
+        GridLayout layout = new GridLayout();
+        composite.setLayout(layout);
+
+        int index = addPage(composite);
+
+        final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
+        table = tableViewer.getTable();
+        table.setLinesVisible(true);
+        table.setHeaderVisible(true);
+        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+        final TableColumn tableColumn = new TableColumn(table, SWT.NONE);
+        tableColumn.setWidth(100);
+        tableColumn.setText("Key");
+
+        final TableColumn tableColumn_1 = new TableColumn(table, SWT.NONE);
+        tableColumn_1.setWidth(1000);
+        tableColumn_1.setText("Value");
+        setPageText(index, "Properties");
+    }
+
+    /**
+     * Creates page 2 of the multi-page editor, which shows the sorted text.
+     */
+    void createPage2() {
+        Composite composite = new Composite(getContainer(), SWT.NONE);
+        FillLayout layout = new FillLayout();
+        composite.setLayout(layout);
+        text = new StyledText(composite, SWT.H_SCROLL | SWT.V_SCROLL);
+        text.setEditable(false);
+
+        int index = addPage(composite);
+        setPageText(index, "Preview");
+    }
+
+    
+    
+    
+    void createPage3() {
+        
+    }
+    /**
+     * Creates the pages of the multi-page editor.
+     */
+    protected void createPages() {
+        createTextEditor();
+        createPage1();
+        createPage2();
+    }
+
+    /**
+     * The <code>MultiPageEditorPart</code> implementation of this
+     * <code>IWorkbenchPart</code> method disposes all nested editors.
+     * Subclasses may extend.
+     */
+    public void dispose() {
+        ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+        super.dispose();
+    }
+
+    /**
+     * Saves the multi-page editor's document.
+     */
+    public void doSave(IProgressMonitor monitor) {
+        getEditor(0).doSave(monitor);
+    }
+
+    /**
+     * Saves the multi-page editor's document as another file. Also updates the
+     * text for page 0's tab, and updates this multi-page editor's input to
+     * correspond to the nested editor's.
+     */
+    public void doSaveAs() {
+        IEditorPart editor = getEditor(0);
+        editor.doSaveAs();
+        setPageText(0, editor.getTitle());
+        setInput(editor.getEditorInput());
+    }
+
+    public void setInput(IEditorInput iei ) {
+	    super.setInput(iei);
+	    setPartName( iei.getName());
+	    setContentDescription(iei.getToolTipText());
+	    if ( iei instanceof IFileEditorInput ) {
+	        IFileEditorInput in = (IFileEditorInput) iei;
+	        IProject project = in.getFile().getProject();
+	        setPartName(project.getName());
+	    }
+	}
+
+    /*
+     * (non-Javadoc) Method declared on IEditorPart
+     */
+    public void gotoMarker(IMarker marker) {
+        setActivePage(0);
+        IDE.gotoMarker(getEditor(0), marker);
+    }
+
+    /**
+     * The <code>MultiPageEditorExample</code> implementation of this method
+     * checks that the input is an instance of <code>IFileEditorInput</code>.
+     */
+    public void init(IEditorSite site, IEditorInput editorInput)
+            throws PartInitException {
+        if (!(editorInput instanceof IFileEditorInput))
+            throw new PartInitException(
+                    "Invalid Input: Must be IFileEditorInput");
+        super.init(site, editorInput);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IEditorPart.
+     */
+    public boolean isSaveAsAllowed() {
+        return true;
+    }
+
+    /**
+     * Calculates the contents of page 2 when the it is activated.
+     */
+    protected void pageChange(int newPageIndex) {
+        super.pageChange(newPageIndex);
+        if (newPageIndex == 2) {
+            sortWords();
+        }
+    }
+
+    /**
+     * Closes all project files on project close.
+     */
+    public void resourceChanged(final IResourceChangeEvent event) {
+        if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
+            Display.getDefault().asyncExec(new Runnable() {
+                public void run() {
+                    IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
+                            .getPages();
+                    for (int i = 0; i < pages.length; i++) {
+                        if (((FileEditorInput) editor.getEditorInput())
+                                .getFile().getProject().equals(
+                                        event.getResource())) {
+                            IEditorPart editorPart = pages[i].findEditor(editor
+                                    .getEditorInput());
+                            pages[i].closeEditor(editorPart, true);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+
+    /**
+     * Sorts the words in page 0, and shows them in page 2.
+     */
+    void sortWords() {
+
+        String editorText = editor.getDocumentProvider().getDocument(
+                editor.getEditorInput()).get();
+
+        StringTokenizer tokenizer = new StringTokenizer(editorText,
+                " \t\n\r\f!@#\u0024%^&*()-_=+`~[]{};:'\",.<>/?|\\");
+        ArrayList editorWords = new ArrayList();
+        while (tokenizer.hasMoreTokens()) {
+            editorWords.add(tokenizer.nextToken());
+        }
+
+        Collections.sort(editorWords, Collator.getInstance());
+        StringWriter displayText = new StringWriter();
+        for (int i = 0; i < editorWords.size(); i++) {
+            displayText.write(((String) editorWords.get(i)));
+            displayText.write(System.getProperty("line.separator"));
+        }
+        text.setText(displayText.toString());
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditorContributor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditorContributor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndMultiPageEditorContributor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,103 @@
+package aQute.bnd.plugin.editors;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.ide.IDEActionFactory;
+import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.ITextEditorActionConstants;
+
+/**
+ * Manages the installation/deinstallation of global actions for multi-page editors.
+ * Responsible for the redirection of global actions to the active editor.
+ * Multi-page contributor replaces the contributors for the individual editors in the multi-page editor.
+ */
+public class BndMultiPageEditorContributor extends MultiPageEditorActionBarContributor {
+	private IEditorPart activeEditorPart;
+	private Action sampleAction;
+	/**
+	 * Creates a multi-page contributor.
+	 */
+	public BndMultiPageEditorContributor() {
+		super();
+		createActions();
+	}
+	/**
+	 * Returns the action registed with the given text editor.
+	 * @return IAction or null if editor is null.
+	 */
+	protected IAction getAction(ITextEditor editor, String actionID) {
+		return (editor == null ? null : editor.getAction(actionID));
+	}
+	/* (non-JavaDoc)
+	 * Method declared in AbstractMultiPageEditorActionBarContributor.
+	 */
+
+	public void setActivePage(IEditorPart part) {
+		if (activeEditorPart == part)
+			return;
+
+		activeEditorPart = part;
+
+		IActionBars actionBars = getActionBars();
+		if (actionBars != null) {
+
+			ITextEditor editor = (part instanceof ITextEditor) ? (ITextEditor) part : null;
+
+			actionBars.setGlobalActionHandler(
+				ActionFactory.DELETE.getId(),
+				getAction(editor, ITextEditorActionConstants.DELETE));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.UNDO.getId(),
+				getAction(editor, ITextEditorActionConstants.UNDO));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.REDO.getId(),
+				getAction(editor, ITextEditorActionConstants.REDO));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.CUT.getId(),
+				getAction(editor, ITextEditorActionConstants.CUT));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.COPY.getId(),
+				getAction(editor, ITextEditorActionConstants.COPY));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.PASTE.getId(),
+				getAction(editor, ITextEditorActionConstants.PASTE));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.SELECT_ALL.getId(),
+				getAction(editor, ITextEditorActionConstants.SELECT_ALL));
+			actionBars.setGlobalActionHandler(
+				ActionFactory.FIND.getId(),
+				getAction(editor, ITextEditorActionConstants.FIND));
+			actionBars.setGlobalActionHandler(
+				IDEActionFactory.BOOKMARK.getId(),
+				getAction(editor, IDEActionFactory.BOOKMARK.getId()));
+			actionBars.updateActionBars();
+		}
+	}
+	private void createActions() {
+		sampleAction = new Action() {
+			public void run() {
+				MessageDialog.openInformation(null, "aQute Bnd PRO", "Sample Action Executed");
+			}
+		};
+		sampleAction.setText("Sample Action");
+		sampleAction.setToolTipText("Sample Action tool tip");
+		sampleAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
+				getImageDescriptor(IDE.SharedImages.IMG_OBJS_TASK_TSK));
+	}
+	public void contributeToMenu(IMenuManager manager) {
+		IMenuManager menu = new MenuManager("Editor &Menu");
+		manager.prependToGroup(IWorkbenchActionConstants.MB_ADDITIONS, menu);
+		menu.add(sampleAction);
+	}
+	public void contributeToToolBar(IToolBarManager manager) {
+		manager.add(new Separator());
+		manager.add(sampleAction);
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndScanner.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndScanner.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndScanner.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,76 @@
+package aQute.bnd.plugin.editors;
+
+import java.util.*;
+
+import org.eclipse.jface.text.rules.*;
+
+import aQute.bnd.make.*;
+import aQute.lib.osgi.*;
+
+public class BndScanner extends RuleBasedScanner {
+    BndSourceViewerConfiguration bsvc;
+    
+
+    public BndScanner(BndSourceViewerConfiguration manager) {
+        bsvc = manager;
+        IRule[] rules = new IRule[] {
+                new WhitespaceRule(new IWhitespaceDetector() {
+                    public boolean isWhitespace(char c) {
+                        return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+                    }
+                }), new BndWordRule(), new MacroRule(bsvc.T_MACRO),
+                new EndOfLineRule("#", bsvc.T_COMMENT),
+                new EndOfLineRule("\\ ", bsvc.T_ERROR),
+                new EndOfLineRule("\\\t", bsvc.T_ERROR), };
+
+        setRules(rules);
+        setDefaultReturnToken(bsvc.T_DEFAULT);
+    }
+    
+    class BndWordRule implements IRule {
+
+        Map<String, IToken> keyWords = new HashMap<String, IToken>();
+
+        public BndWordRule() {
+            addWords(Analyzer.headers, bsvc.T_INSTRUCTION);
+            addWords(Analyzer.options, bsvc.T_OPTION);
+            addWords(Analyzer.directives, bsvc.T_DIRECTIVE);
+            addWords(ServiceComponent.componentDirectives, bsvc.T_COMPONENT);
+        }
+
+        private boolean isWordStart(char c) {
+            return Character.isJavaIdentifierStart(c);
+        }
+
+        private boolean isWordPart(char c) {
+            return Character.isJavaIdentifierPart(c) || c == '-';
+        }
+
+        public IToken evaluate(ICharacterScanner scanner) {
+            StringBuffer sb = new StringBuffer();
+
+            int c = scanner.read();
+            if (isWordStart((char) c) || c == '-') {
+                do {
+                    sb.append((char) c);
+                    c = scanner.read();
+                } while (c != ICharacterScanner.EOF && isWordPart((char) c));
+                scanner.unread();
+
+                IToken token = (IToken) keyWords.get(sb.toString());
+                if (token != null)
+                    return token;
+                return bsvc.T_DEFAULT;
+            }
+            scanner.unread();
+            return Token.UNDEFINED;
+
+        }
+
+        private void addWords(String[] words, IToken token) {
+            for (int i = 0; i < words.length; ++i) {
+                keyWords.put(words[i], token);
+            }
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndSourceViewerConfiguration.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndSourceViewerConfiguration.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndSourceViewerConfiguration.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,129 @@
+package aQute.bnd.plugin.editors;
+
+import java.util.*;
+
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.contentassist.*;
+import org.eclipse.jface.text.presentation.*;
+import org.eclipse.jface.text.rules.*;
+import org.eclipse.jface.text.source.*;
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+
+public class BndSourceViewerConfiguration extends SourceViewerConfiguration {
+
+    Token T_DEFAULT;
+    Token T_MACRO;
+    Token T_ERROR;
+    Token T_COMMENT;
+    Token T_INSTRUCTION;
+    Token T_OPTION;
+    Token T_DIRECTIVE;
+    Token T_PROPERTY;
+    Token T_COMPONENT;
+    
+    static final String SINGLELINE_COMMENT_TYPE = "___slc";
+    static Properties   syntax                  = null;
+
+    class BndPartitionScanner extends RuleBasedPartitionScanner {
+        public BndPartitionScanner() {
+            IToken singleLineComment = new Token(SINGLELINE_COMMENT_TYPE);
+
+            IPredicateRule[] rules = new IPredicateRule[] { new EndOfLineRule(
+                    "#", singleLineComment), };
+            setPredicateRules(rules);
+        }
+    }
+
+    BndScanner              scanner;
+    MultiLineCommentScanner multiLineCommentScanner;
+    ISharedTextColors       colors;
+
+    public BndSourceViewerConfiguration(ISharedTextColors colors) {
+        this.colors = colors;
+        T_DEFAULT = new Token(new TextAttribute(colors.getColor(new RGB(0, 0,
+                0))));
+        T_MACRO = new Token(new TextAttribute(colors.getColor(new RGB(0, 255,
+                0)), null, SWT.BOLD));
+        T_ERROR = new Token(new TextAttribute(colors.getColor(new RGB(255, 0,
+                0)), null, SWT.BOLD));
+        T_COMMENT = new Token(new TextAttribute(colors.getColor(new RGB(128,
+                0, 0))));
+        T_INSTRUCTION = new Token(new TextAttribute(colors.getColor(new RGB(0,
+                0, 255)), null, SWT.BOLD));
+        T_OPTION = new Token(new TextAttribute(colors.getColor(new RGB(0, 0,
+                255))));
+        T_DIRECTIVE = new Token(new TextAttribute(colors.getColor(new RGB(60,
+                60, 255)), null, SWT.BOLD));
+        T_PROPERTY = new Token(new TextAttribute(colors.getColor(new RGB(60,
+                60, 255)), null, SWT.BOLD));
+        T_COMPONENT = new Token(new TextAttribute(colors.getColor(new RGB(60,
+                60, 255)), null, SWT.BOLD));
+    }
+
+    public IPresentationReconciler getPresentationReconciler(
+            ISourceViewer sourceViewer) {
+        PresentationReconciler reconciler = new PresentationReconciler();
+        configureReconciler(reconciler, IDocument.DEFAULT_CONTENT_TYPE,
+                getBndScanner());
+        configureReconciler(reconciler, SINGLELINE_COMMENT_TYPE,
+                getMultiLineCommentScanner());
+        return reconciler;
+    }
+
+    private void configureReconciler(PresentationReconciler reconciler,
+            String partitionType, ITokenScanner scanner) {
+        DefaultDamagerRepairer dr;
+        dr = new DefaultDamagerRepairer(scanner);
+        reconciler.setDamager(dr, partitionType);
+        reconciler.setRepairer(dr, partitionType);
+    }
+
+    protected BndScanner getBndScanner() {
+        if (scanner == null) {
+            scanner = new BndScanner(this);
+        }
+        return scanner;
+    }
+
+    class MultiLineCommentScanner extends RuleBasedScanner {
+
+        public MultiLineCommentScanner() {
+            setDefaultReturnToken(T_COMMENT);
+        }
+
+    }
+
+    protected MultiLineCommentScanner getMultiLineCommentScanner() {
+        if (multiLineCommentScanner == null) {
+            multiLineCommentScanner = new MultiLineCommentScanner();
+        }
+        return multiLineCommentScanner;
+    }
+
+    public String[] getDefaultPrefixes(ISourceViewer sourceViewer,
+            String contentType) {
+        if (IDocument.DEFAULT_CONTENT_TYPE == contentType
+                || SINGLELINE_COMMENT_TYPE == contentType) {
+            return new String[] { "#", "//" };
+        }
+        return null;
+    }
+
+    @Override
+    public ITextHover getTextHover(ISourceViewer sourceViewer,
+            String contentType) {
+        return new BndHover();
+    }
+
+    @Override
+    public IContentAssistant getContentAssistant(ISourceViewer viewer) {
+        ContentAssistant assistant = new ContentAssistant();
+        assistant.setContentAssistProcessor(new BndCompletionProcessor(),
+                IDocument.DEFAULT_CONTENT_TYPE);
+        assistant.setContentAssistProcessor(new BndCompletionProcessor(),
+                SINGLELINE_COMMENT_TYPE);
+        assistant.enableAutoActivation(true);
+        return assistant;
+    }
+}
\ No newline at end of file

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndTextEditor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndTextEditor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/BndTextEditor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.plugin.editors;
+
+import org.eclipse.ui.editors.text.*;
+
+public class BndTextEditor extends TextEditor {
+	BndTextEditor() {
+		setSourceViewerConfiguration(new BndSourceViewerConfiguration(getSharedColors()));
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/MacroRule.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/MacroRule.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/editors/MacroRule.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,58 @@
+package aQute.bnd.plugin.editors;
+
+import org.eclipse.jface.text.rules.*;
+
+import aQute.lib.osgi.*;
+
+public class MacroRule implements IRule {
+
+    private StringBuffer buffer = new StringBuffer();
+    private IToken       token;
+
+    public MacroRule(IToken token) {
+        this.token = token;
+    }
+
+    public IToken evaluate(ICharacterScanner scanner) {
+        int c = scanner.read();
+        if (c == '$') {
+            buffer.setLength(0);
+            buffer.append('$');
+            if ( scan(scanner, buffer) )
+                return token;
+        }
+        scanner.unread();
+        return Token.UNDEFINED;
+
+    }
+
+    boolean scan( ICharacterScanner scanner, StringBuffer buffer ) {
+        int c = scanner.read();
+        if (c == ICharacterScanner.EOF)
+            return false;
+        int terminator = Macro.getTerminator((char) c);
+        
+        if (terminator == 0)
+            return false;
+
+
+        while(true) {
+            c = scanner.read();
+            buffer.append((char)c);
+            if ( c == terminator )
+                return true;
+            else
+            if ( c == '$') {
+                if ( !scan(scanner, buffer) )
+                    return false;
+            }
+            else         
+            if ( c == '\\') {
+                c = scanner.read();
+                if ( c == ICharacterScanner.EOF)
+                    return false;
+                buffer.append((char)c);
+            }
+        }
+   }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/Scripts.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/Scripts.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/Scripts.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,134 @@
+package aQute.bnd.plugin.popup;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.*;
+import org.eclipse.jdt.core.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+import org.eclipse.ui.actions.*;
+import org.eclipse.ui.console.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+
+public class Scripts extends CompoundContributionItem {
+    final List<Project> projects = new ArrayList<Project>();
+    static IOConsole    console;
+
+    public Scripts() {
+        ISelectionService is = Activator.getDefault().getWorkbench()
+                .getActiveWorkbenchWindow().getSelectionService();
+        ISelection s = is.getSelection();
+        if (s != null && s instanceof IStructuredSelection) {
+            IStructuredSelection ss = (IStructuredSelection) s;
+            for (Iterator<?> i = ss.iterator(); i.hasNext();) {
+                Object oo = i.next();
+                IJavaProject jp = null;
+                if (oo instanceof IResource) {
+                    IResource r = (IResource) oo;
+                    IProject iproject = r.getProject();
+                    jp = JavaCore.create(iproject);
+                } else if (oo instanceof IJavaProject) {
+                    jp = (IJavaProject) oo;
+                }
+
+                if (jp != null) {
+                	if (!jp.getProject().isAccessible()) {
+                		continue;
+                	}
+                	
+                	Project project = Activator.getDefault().getCentral().getModel(jp);
+                	
+                	File bndFile = project.getFile("bnd.bnd");
+                	if (!bndFile.exists()) {
+                		continue;
+                	}
+                	projects.add(project);
+                }
+            }
+        }
+    }
+
+    public Scripts(String id) {
+        super(id);
+    }
+
+    @Override
+    protected IContributionItem[] getContributionItems() {
+        if (projects.isEmpty())
+            return new IContributionItem[0];
+
+        Set<String> titles = new HashSet<String>();
+        boolean first = true;
+        for (Project project : projects) {
+            if (first) {
+                titles.addAll(project.getActions().keySet());
+                first = false;
+            } else {
+                titles.retainAll(project.getActions().keySet());
+            }
+        }
+        
+        SubMenu root = new SubMenu("root");
+        
+        SubMenu sub = new SubMenu("Bnd");
+        
+        root.add(sub);
+        
+        for (final String title : titles) {
+            sub.add(this,title,title);
+        }
+        
+        return root.getItems();
+    }
+
+
+    void exec(final String label) {
+        Job job = new Job(label) {
+            protected IStatus run(IProgressMonitor monitor) {
+
+                for (Project project : projects) {
+                    if (monitor != null) {
+                        if (monitor.isCanceled())
+                            break;
+                        monitor.subTask("" + project + " " + label);
+                    }
+
+                    Map<String, aQute.bnd.service.action.Action> actions = project.getActions();
+                    aQute.bnd.service.action.Action cmd = actions.get(label);
+                    try {
+                        cmd.execute(project,label);
+                        monitor.worked(1);
+
+                        Activator.getDefault().getCentral().refresh(project);
+                        if (!project.isPerfect()) {
+
+                            // We had errors or warnings
+                            Activator.getDefault().report(true, true, project,
+                                    "During execution of " + label, "");
+                            return Status.CANCEL_STATUS;
+                        }
+                    } catch (Exception e) {
+                        Activator.getDefault().error(
+                                "While executing action: " + cmd, e);
+                    }
+                }
+                try {
+                    Activator.getDefault().getCentral().refreshPlugins();
+                    return Status.OK_STATUS;
+                } catch (Exception e) {
+                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+                            "Failed to refresh: " + e);
+                }
+            }
+        };
+        job.setPriority(Job.SHORT);
+        job.schedule(); // start as soon as possible
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/SubMenu.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/SubMenu.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/SubMenu.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,74 @@
+package aQute.bnd.plugin.popup;
+
+import java.util.*;
+
+import org.eclipse.jface.action.*;
+
+public class SubMenu extends MenuManager {
+    Map<String, IContributionItem> items = new LinkedHashMap<String, IContributionItem>();
+
+    SubMenu(String name) {
+        super(name);
+    }
+
+    @Override
+    public IContributionItem[] getItems() {
+        return items.values().toArray(new IContributionItem[items.size()]);
+    }
+
+    void add(final Scripts script, final String s, final String full) {
+        int n = s.indexOf(':');
+        if (n < 0) {
+            n = s.indexOf(">");
+            if (n < 0) {
+                items.put(s, new ActionContributionItem(new Action() {
+                    {
+                        setText(s);
+                    }
+
+                    public void run() {
+                        script.exec(full);
+                    }
+                }));
+            } else {
+                String name = s.substring(0,n);
+                String remainder = s.substring(n+1);
+                IContributionItem ici = items.get(name);
+                if (ici == null) {
+                    ici = new SubMenu(name);
+                    items.put(name, ici);
+                }
+                if (!(ici instanceof SubMenu)) {
+                    // A leaf & node ... :-(
+                } else {
+                    SubMenu sub = (SubMenu) ici;
+                    String parts[] = remainder.split(",");
+                    for ( String part : parts ) {
+                        sub.add(script, part, full);
+                    }
+                }
+                
+            }
+        } else {
+            String name = s.substring(0, n);
+            IContributionItem ici = items.get(name);
+            if (ici == null) {
+                ici = new SubMenu(name);
+                items.put(name, ici);
+            }
+            if (!(ici instanceof SubMenu)) {
+                // A leaf & node ... :-(
+            } else {
+                SubMenu sub = (SubMenu) ici;
+                sub.add(script, s.substring(n + 1), full);
+            }
+        }
+    }
+    
+    void add(SubMenu subMenu) {
+    	IContributionItem ici = items.get(subMenu.getMenuText());
+        if (ici == null) {
+            items.put(subMenu.getMenuText(), subMenu);
+        }
+   }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/AddToRepo.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/AddToRepo.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/AddToRepo.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,96 @@
+package aQute.bnd.plugin.popup.actions;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+import aQute.bnd.plugin.popup.actions.repo.*;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class AddToRepo implements IObjectActionDelegate {
+    IFile[] locations;
+
+    public AddToRepo() {
+    }
+
+    /**
+     * @see IActionDelegate#run(IAction)
+     */
+    public void run(IAction action) {
+        try {
+            if (locations != null) {
+                for (int i = 0; i < locations.length; i++) {
+                    File mf = locations[i].getLocation().toFile();
+                    try {
+                        
+                        Jar jar = new Jar(mf.getName(), mf);
+                        
+                        try {
+                            Verifier verifier = new Verifier(jar);
+                            verifier.verify();
+                            if (verifier.getErrors().size()
+                                    + verifier.getWarnings().size() > 0) {
+                                List<String> info = new ArrayList<String>(verifier.getErrors());
+                                info.addAll(verifier.getWarnings());
+                                Activator.getDefault().error(info);
+                            } else {
+                                Workspace ws = Central.getWorkspace();
+                                List<RepositoryPlugin> repos = ws.getPlugins(RepositoryPlugin.class);
+                                RepoDialog d = new RepoDialog(null, jar, repos );                                
+                                d.setBlockOnOpen(true);
+                                if ( d.open() == 0 ) {
+                                    RepositoryPlugin repo = d.getRepository();
+                                    File f = repo.put(jar);
+                                    Central.refresh(Path.fromOSString(f.getAbsolutePath()));
+                                }
+                                
+                            }
+                        } finally {
+                            jar.close();
+                        }
+                        
+                    } catch (Exception e) {
+                        Activator.getDefault().error(
+                                "While verifying JAR " + locations[i], e);
+                    }
+                    locations[i].getParent().refreshLocal(1, null);
+                }
+            }
+        } catch (Exception e) {
+            Activator.getDefault().error("Could not start verification", e);
+        }
+    }
+
+    /**
+     * @see IActionDelegate#selectionChanged(IAction, ISelection)
+     */
+    public void selectionChanged(IAction action, ISelection selection) {
+        locations = getLocations(selection);
+    }
+
+    @SuppressWarnings("unchecked")
+    IFile[] getLocations(ISelection selection) {
+        if (selection != null && (selection instanceof StructuredSelection)) {
+            StructuredSelection ss = (StructuredSelection) selection;
+            IFile[] result = new IFile[ss.size()];
+            int n = 0;
+            for (Iterator<IFile> i = ss.iterator(); i.hasNext();) {
+                result[n++] = i.next();
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/InstallBundle.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/InstallBundle.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/InstallBundle.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,127 @@
+package aQute.bnd.plugin.popup.actions;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+import org.osgi.framework.*;
+import org.osgi.service.packageadmin.*;
+
+import aQute.bnd.plugin.*;
+
+public class InstallBundle implements IObjectActionDelegate {
+	IFile[]	locations;
+
+	public InstallBundle() {
+	}
+
+	/**
+	 * @see IActionDelegate#run(IAction)
+	 */
+	public void run(IAction action) {
+		Activator activator = Activator.getDefault();
+		Map<String,Bundle> map = new HashMap<String, Bundle>();
+		BundleContext context = activator.getBundle().getBundleContext();
+		Bundle bundles[] = context.getBundles();
+		for (int i = 0; i < bundles.length; i++) {
+			map.put(bundles[i].getLocation(), bundles[i]);
+		}
+
+		if (locations != null) {
+			List<Bundle> installed = new ArrayList<Bundle>();
+			List<Bundle> updated = new ArrayList<Bundle>();
+			int errors = 0;
+			for (int i = 0; i < locations.length; i++) {
+				try {
+					File mf = locations[i].getLocation().toFile();
+					String url = mf.toURL().toExternalForm();
+					Bundle b = (Bundle) map.get(url);
+					if (b != null) {
+						b.update();
+						updated.add(b);
+					} else {
+						b = context.installBundle(url);
+						installed.add(b);
+					}
+				} catch (Exception e) {
+					errors++;
+					Activator.getDefault()
+							.error("Error during install/update ", e);
+				}
+			}
+			if ( !updated.isEmpty()) {
+				ServiceReference ref = context.getServiceReference(PackageAdmin.class.getName());
+				if ( ref != null ) {
+					PackageAdmin packageAdmin = (PackageAdmin) context.getService(ref);
+					if ( packageAdmin != null ) {
+						packageAdmin.refreshPackages((Bundle[])updated.toArray(new Bundle[updated.size()]));
+					} else
+						activator.error("Can't get Package Admin service to refresh", null);
+				}  else
+					activator.error("No Package Admin to refresh", null);
+			}
+			StringBuffer sb = new StringBuffer();
+			printBundles("Installed Bundles", installed, sb);		
+			printBundles("Updated Bundles", updated, sb);
+			switch(errors) {
+			case 0: break;
+			case 1: sb.append("One Error\n"); break;
+			default: sb.append(errors); sb.append(" Errors\n");
+			}
+			activator.message(sb.toString());
+		}
+	}
+
+	private void printBundles(String msg, List<Bundle> list, StringBuffer sb) {
+		if ( list.isEmpty() )
+			return;
+		
+		sb.append(msg);
+		sb.append('\n');
+		for ( Bundle  b : list ) {
+			String version = (String) b.getHeaders().get("Bundle-Version");
+			if ( version == null )
+				version = "0.0.0";
+			
+			String name = b.getSymbolicName();
+			if ( name == null )
+				name = b.getLocation();
+			
+			sb.append("  ");
+			sb.append(name);
+			for ( int p = name.length(); p<20; p++ )
+				sb.append(" ");
+			sb.append("- ");
+			sb.append(version);
+			sb.append("\n");
+		}
+	}
+
+	/**
+	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
+	 */
+	public void selectionChanged(IAction action, ISelection selection) {
+		locations = getLocations(selection);
+	}
+
+	@SuppressWarnings("unchecked")
+    IFile[] getLocations(ISelection selection) {
+		if (selection != null && (selection instanceof StructuredSelection)) {
+			StructuredSelection ss = (StructuredSelection) selection;
+			IFile[] result = new IFile[ss.size()];
+			int n = 0;
+			for (Iterator<IFile> i = ss.iterator(); i.hasNext();) {
+				result[n++] = (IFile) i.next();
+			}
+			return result;
+		}
+		return null;
+	}
+
+	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/MakeBundle.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/MakeBundle.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/MakeBundle.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,160 @@
+package aQute.bnd.plugin.popup.actions;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.plugin.*;
+import aQute.lib.osgi.*;
+import aQute.lib.osgi.eclipse.*;
+
+public class MakeBundle implements IObjectActionDelegate {
+    IFile[] locations;
+
+    public MakeBundle() {
+    }
+
+    /**
+     * @see IActionDelegate#run(IAction)
+     */
+    public void run(IAction action) {
+        Activator activator = Activator.getDefault();
+
+        try {
+            if (locations != null) {
+                for (int i = 0; i < locations.length; i++) {
+                    try {
+                        File mf = locations[i].getLocation().toFile();
+                        if (mf.getName().equals(Project.BNDFILE)) {
+                            Project project = Workspace.getProject(mf
+                                    .getParentFile());
+                            File[] files = project.build();
+                            String target = "";
+                            if ( files != null ) {
+                                for ( File f : files ) {
+                                    target += f + " ";
+                                    
+                                }
+                            }
+                            activator.report(true, true, project, "Building "
+                                    + project, "Created files " + target );
+                        } else {
+                            Builder builder = setBuilder(activator,
+                                    locations[i].getProject(), mf);
+
+                            File cwd = mf.getAbsoluteFile().getParentFile();
+                            File target;
+
+                            builder.build();
+                            String name = builder.getBsn() + ".jar";
+
+                            Jar jar = builder.getJar();
+
+                            String path = builder.getProperty("-output");
+                            if (path == null) {
+                                target = new File(cwd, name);
+                            } else {
+                                target = new File(path);
+                                if (!target.isAbsolute())
+                                    target = new File(cwd, path);
+                                if (target.isDirectory()) {
+                                    target = new File(target, name);
+                                }
+                            }
+
+                            target.delete();
+                            if (builder.getErrors().size() > 0) {
+                                activator.error(builder.getErrors());
+                            } else {
+                                jar.write(target);
+
+                                File copy = activator.getCopy();
+                                if (copy != null) {
+                                    copy = new File(copy, target.getName());
+                                    jar.write(copy);
+                                }
+                                if (builder.getWarnings().size() > 0) {
+                                    activator.warning(builder.getWarnings());
+                                } else {
+                                    if (activator.getReportDone()) {
+                                        String p = target.getPath();
+                                        if (p.startsWith(cwd.getAbsolutePath()))
+                                            p = p
+                                                    .substring(cwd
+                                                            .getAbsolutePath()
+                                                            .length() + 1);
+                                        String msg = "Saved as " + p;
+                                        if (copy != null)
+                                            msg += " and copied to " + copy;
+                                        activator.message(msg);
+                                    }
+                                }
+                            }
+                            builder.close();
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        activator.error("While generating JAR " + locations[i],
+                                e);
+                    }
+                    locations[i].getParent().refreshLocal(1, null);
+                }
+            }
+        } catch (Exception e) {
+            activator.error("Error in bnd", e);
+        }
+    }
+
+    static public Builder setBuilder(Activator activator, IProject project,
+            File mf) throws Exception, IOException, FileNotFoundException {
+        Builder builder = new Builder();
+        builder.setPedantic(activator.isPedantic() || activator.isDebugging());
+
+        // TODO of course we should get the classpath from
+        // inside API ...
+        File p = project.getLocation().toFile();
+
+        // TODO for now we ignore the workspace and use the
+        // project parent directory
+
+        EclipseClasspath ecp = new EclipseClasspath(builder, p.getParentFile(),
+                p);
+
+        builder.setClasspath((File[]) ecp.getClasspath().toArray(new File[0]));
+        builder
+                .setSourcepath((File[]) ecp.getSourcepath()
+                        .toArray(new File[0]));
+        builder.setProperties(mf);
+        return builder;
+    }
+
+    /**
+     * @see IActionDelegate#selectionChanged(IAction, ISelection)
+     */
+    public void selectionChanged(IAction action, ISelection selection) {
+        locations = getLocations(selection);
+    }
+
+    @SuppressWarnings("unchecked")
+    IFile[] getLocations(ISelection selection) {
+        if (selection != null && (selection instanceof StructuredSelection)) {
+            StructuredSelection ss = (StructuredSelection) selection;
+            IFile[] result = new IFile[ss.size()];
+            int n = 0;
+            for (Iterator<IFile> i = ss.iterator(); i.hasNext();) {
+                result[n++] = i.next();
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/VerifyBundle.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/VerifyBundle.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/VerifyBundle.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,80 @@
+package aQute.bnd.plugin.popup.actions;
+
+import java.io.File;
+import java.util.*;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+
+import aQute.bnd.plugin.Activator;
+import aQute.lib.osgi.*;
+
+public class VerifyBundle implements IObjectActionDelegate {
+    IFile[] locations;
+
+    public VerifyBundle() {
+    }
+
+    /**
+     * @see IActionDelegate#run(IAction)
+     */
+    public void run(IAction action) {
+        try {
+            if (locations != null) {
+                for (int i = 0; i < locations.length; i++) {
+                    File mf = locations[i].getLocation().toFile();
+                    try {
+                        Jar jar = new Jar(mf.getName(), mf);
+                        Verifier verifier = new Verifier(jar);
+                        try {
+                            verifier.verify();
+                            if (verifier.getErrors().size()
+                                    + verifier.getWarnings().size() > 0) {
+                                List<String> info = new ArrayList<String>(verifier.getErrors());
+                                info.addAll(verifier.getWarnings());
+                                Activator.getDefault().error(info);
+                            }
+                        } finally {
+                            jar.close();
+                            verifier.close();
+                        }
+
+                    } catch (Exception e) {
+                        Activator.getDefault().error(
+                                "While verifying JAR " + locations[i], e);
+                    }
+                    locations[i].getParent().refreshLocal(1, null);
+                }
+            }
+        } catch (Exception e) {
+            Activator.getDefault().error("Could not start verification", e);
+        }
+    }
+
+    /**
+     * @see IActionDelegate#selectionChanged(IAction, ISelection)
+     */
+    public void selectionChanged(IAction action, ISelection selection) {
+        locations = getLocations(selection);
+    }
+
+    @SuppressWarnings("unchecked")
+    IFile[] getLocations(ISelection selection) {
+        if (selection != null && (selection instanceof StructuredSelection)) {
+            StructuredSelection ss = (StructuredSelection) selection;
+            IFile[] result = new IFile[ss.size()];
+            int n = 0;
+            for (Iterator<IFile> i = ss.iterator(); i.hasNext();) {
+                result[n++] = i.next();
+            }
+            return result;
+        }
+        return null;
+    }
+
+    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/WrapBundle.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/WrapBundle.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/WrapBundle.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,60 @@
+package aQute.bnd.plugin.popup.actions;
+
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.ui.*;
+
+import aQute.bnd.plugin.*;
+
+public class WrapBundle implements IObjectActionDelegate {
+	IFile[]		locations;
+	public WrapBundle(){}
+
+	/**
+	 * @see IActionDelegate#run(IAction)
+	 */
+	public void run(IAction action) {
+		try {
+			if ( locations!= null) {
+				for (int i = 0; i < locations.length; i++) {
+					// TODO
+					MessageDialog.openInformation(null,"Not Implemented Yet", "TODO implement wrapping");
+				}
+			}
+		}
+		catch( Exception e ) {
+			Activator.getDefault().error("Could not start Test View", e );
+		}
+	}
+
+	 
+	 /**
+		 * @see IActionDelegate#selectionChanged(IAction, ISelection)
+		 */
+	public void selectionChanged(IAction action, ISelection selection) {
+		locations = getLocations(selection);
+	}
+
+
+	@SuppressWarnings("unchecked")
+    IFile[] getLocations(ISelection selection) {
+		if (selection != null && (selection instanceof StructuredSelection)) {
+			StructuredSelection ss = (StructuredSelection) selection;
+			IFile[] result = new IFile[ss.size()];
+			int n = 0;
+			for (Iterator<IFile> i = ss.iterator(); i.hasNext();) {
+				result[n++] = i.next();
+			}
+			return result;
+		}
+		return null;
+	}
+
+	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/repo/RepoDialog.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/repo/RepoDialog.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/plugin/popup/actions/repo/RepoDialog.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,147 @@
+package aQute.bnd.plugin.popup.actions.repo;
+
+import java.util.*;
+import java.util.jar.*;
+
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+
+public class RepoDialog extends Dialog {
+    private Text                             bsn;
+    private Text                             version;
+    private Label                            lblRepository;
+    private Combo                            repositories;
+    private Jar                              jar;
+    private java.util.List<RepositoryPlugin> repos;
+    private Button                           wrap;
+
+    /**
+     * Create the dialog.
+     * 
+     * @param parentShell
+     */
+    public RepoDialog(Shell parentShell, Jar jar,
+            java.util.List<RepositoryPlugin> repos) {
+        super(parentShell);
+        setShellStyle(SWT.DIALOG_TRIM);
+        this.jar = jar;
+        this.repos = repos;
+    }
+
+    /**
+     * Create contents of the dialog.
+     * 
+     * @param parent
+     */
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        container.setLayout(null);
+        {
+            lblRepository = new Label(container, SWT.NONE);
+            lblRepository.setBounds(8, 8, 160, 24);
+            lblRepository.setText("Repository");
+        }
+        {
+            repositories = new Combo(container, SWT.READ_ONLY);
+            repositories.setBounds(168, 3, 272, 30);
+        }
+        {
+            bsn = new Text(container, SWT.BORDER);
+            bsn.setBounds(168, 35, 272, 28);
+        }
+        {
+            version = new Text(container, SWT.BORDER);
+            version.setBounds(168, 67, 272, 28);
+        }
+        {
+            Label lblBundleSymbolicName = new Label(container, SWT.NONE);
+            lblBundleSymbolicName.setBounds(8, 40, 160, 24);
+            lblBundleSymbolicName.setText("Bundle Symbolic Name");
+        }
+        {
+            Label lblVersion = new Label(container, SWT.NONE);
+            lblVersion.setBounds(8, 72, 160, 24);
+            lblVersion.setText("Bundle Version");
+        }
+
+        {
+            wrap = new Button(container, SWT.NONE);
+            wrap.setBounds(338, 105, 102, 28);
+            wrap.setText("Wrap");
+        }
+        
+        setup();
+        return container;
+    }
+
+    private void setup() {
+        try {
+            String bsn = null;
+            String version = null;
+
+            Manifest manifest = jar.getManifest();
+            if (manifest == null) {
+                bsn = jar.getManifest().getMainAttributes().getValue(
+                        Constants.BUNDLE_SYMBOLICNAME);
+                if (bsn != null)
+                    this.bsn.setText(bsn);
+                version = jar.getManifest().getMainAttributes().getValue(
+                        Constants.BUNDLE_VERSION);
+                if (version != null)
+                    this.version.setText(version);
+
+            }
+
+            for (Iterator<RepositoryPlugin> i = repos.iterator(); i.hasNext();) {
+                RepositoryPlugin plugin = i.next();
+                if (plugin.canWrite())
+                    repositories.add(plugin.getName());
+                else
+                    i.remove();
+
+            }
+            repositories.select(0);
+            
+            wrap.setEnabled(bsn != null );
+            this.bsn.setEnabled(bsn !=null);
+            this.version.setEnabled(bsn !=null);
+                
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Create contents of the button bar.
+     * 
+     * @param parent
+     */
+    @Override
+    protected void createButtonsForButtonBar(Composite parent) {
+        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
+                true);
+        createButton(parent, IDialogConstants.CANCEL_ID,
+                IDialogConstants.CANCEL_LABEL, false);
+    }
+
+    /**
+     * Return the initial size of the dialog.
+     */
+    @Override
+    protected Point getInitialSize() {
+        return new Point(450, 300);
+    }
+
+    public RepositoryPlugin getRepository() {
+        int n = repositories.getSelectionIndex();
+
+        return repos.get(n);
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/AnalyzerPlugin.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/AnalyzerPlugin.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/AnalyzerPlugin.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,20 @@
+package aQute.bnd.service;
+
+import aQute.lib.osgi.*;
+
+public interface AnalyzerPlugin {
+
+    /**
+     * This plugin is called after analysis. The plugin is free to modify the
+     * jar and/or change the classpath information (see referred, contained).
+     * This plugin is called after analysis of the JAR but before manifest
+     * generation.
+     * 
+     * @param analyzer
+     * @return true if the classpace has been modified so that the bundle
+     *         classpath must be reanalyzed
+     * @throws Exception
+     */
+
+    boolean analyzeJar(Analyzer analyzer) throws Exception;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/DependencyContributor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/DependencyContributor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/DependencyContributor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+package aQute.bnd.service;
+
+import java.util.*;
+
+import aQute.bnd.build.*;
+
+public interface DependencyContributor {
+    void addDependencies(Project project, Set<String> dependencies);
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/MakePlugin.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/MakePlugin.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/MakePlugin.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,21 @@
+package aQute.bnd.service;
+
+import java.util.*;
+
+import aQute.lib.osgi.*;
+
+public interface MakePlugin {
+
+    /**
+     * This plugin is called when Include-Resource detects a reference to a resource
+     * that it can not find in the file system.
+     * 
+     * @param builder   The current builder
+     * @param source    The source string (i.e. the place where bnd looked)
+     * @param arguments Any arguments on the clause in Include-Resource
+     * @return          A resource or null if no resource could be made
+     * @throws Exception
+     */
+    Resource make(Builder builder, String source, Map<String,String> arguments) throws Exception;
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Plugin.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Plugin.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Plugin.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,31 @@
+package aQute.bnd.service;
+
+import java.util.*;
+
+import aQute.libg.reporter.*;
+
+/**
+ * An optional interface for plugins. If a plugin implements this interface then
+ * it can receive the reminaing attributes and directives given in its clause as
+ * well as the reporter to use.
+ * 
+ */
+public interface Plugin {
+    /**
+     * Give the plugin the remaining properties.
+     * 
+     * When a plugin is declared, the clause can contain extra properties.
+     * All the properties and directives are given to the plugin to use.
+     * 
+     * @param map attributes and directives for this plugin's clause
+     */
+    void setProperties(Map<String,String> map);
+    
+    /**
+     * Set the current reporter. This is called at init time. This plugin
+     * should report all errors and warnings to this reporter.
+     * 
+     * @param processor
+     */
+    void setReporter(Reporter processor);
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Refreshable.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Refreshable.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/Refreshable.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,8 @@
+package aQute.bnd.service;
+
+import java.io.*;
+
+public interface Refreshable {
+    boolean refresh();
+    File getRoot();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/RepositoryPlugin.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/RepositoryPlugin.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/RepositoryPlugin.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,59 @@
+package aQute.bnd.service;
+
+import java.io.*;
+import java.util.*;
+
+import aQute.lib.osgi.*;
+import aQute.libg.version.*;
+
+public interface RepositoryPlugin {
+    /**
+     * Return a URL to a matching version of the given bundle.
+     * 
+     * @param bsn
+     *            Bundle-SymbolicName of the searched bundle
+     * @param range
+     *            Version range for this bundle,"latest" if you only want the
+     *            latest, or null when you want all.
+     * @return A list of URLs sorted on version, lowest version is at index 0.
+     *         null is returned when no files with the given bsn ould be found.
+     * @throws Exception
+     *             when anything goes wrong
+     */
+    File[] get(String bsn, String range) throws Exception;
+    
+    /**
+     * Answer if this repository can be used to store files.
+     * 
+     * @return true if writable
+     */
+    boolean canWrite();
+    
+    /**
+     * Put a JAR file in the repository.
+     * 
+     * @param jar
+     * @throws Exception
+     */
+    File  put(Jar jar) throws Exception;
+    
+    /**
+     * Return a list of bsns that are present in the repository.
+     * 
+     * @param regex if not null, match against the bsn and if matches, return otherwise skip
+     * @return A list of bsns that match the regex parameter or all if regex is null
+     */
+    List<String> list(String regex);
+    
+    /**
+     * Return a list of versions.
+     */
+    
+    List<Version> versions(String bsn);
+    
+    /**
+     * @return The name of the repository
+     */
+    String getName();
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/SignerPlugin.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/SignerPlugin.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/SignerPlugin.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,15 @@
+package aQute.bnd.service;
+
+import aQute.lib.osgi.*;
+
+public interface SignerPlugin {
+    /**
+     * Sign the current jar. The alias is the given certificate 
+     * keystore.
+     * 
+     * @param builder   The current builder that contains the jar to sign
+     * @param alias     The keystore certificate alias
+     * @throws Exception When anything goes wrong
+     */
+    void sign(Builder builder, String alias) throws Exception;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/Action.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/Action.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/Action.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+package aQute.bnd.service.action;
+
+import aQute.bnd.build.*;
+
+public interface Action {
+    void execute( Project project, String action) throws Exception;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/NamedAction.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/NamedAction.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/service/action/NamedAction.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,6 @@
+package aQute.bnd.service.action;
+
+
+public interface NamedAction extends Action {
+    String getName();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/set/Group.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/set/Group.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/set/Group.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,84 @@
+package aQute.bnd.set;
+
+import java.io.*;
+import java.util.*;
+
+import aQute.lib.osgi.*;
+
+public class Group extends Processor {
+    List<Analyzer> analyzers = new ArrayList<Analyzer>();
+
+    public Group(Processor parent) {
+        super(parent);
+    }
+
+    void add(File jar) throws Exception {
+        add(new Jar(jar));
+    }
+
+    void add(Jar jar) throws Exception {
+        Analyzer analyzer = new Analyzer(this);
+        analyzer.setJar(jar);
+        analyzer.analyze();
+        analyzers.add(analyzer);
+    }
+
+    void analyze() {
+        circular();
+    }
+
+    Map<String, List<Analyzer>> exports;
+
+    void circular() {
+        // Create an index for all exporters package -> analyzers
+        for (Analyzer analyzer : analyzers) {
+            Set<String> ps = analyzer.getExports().keySet();
+            for (String p : ps) {
+                if (exports.containsKey(p))
+                    exports.get(p).add(analyzer);
+                else
+                    exports.put(p, Arrays.asList(analyzer));
+            }
+        }
+
+        List<String> paths = new ArrayList<String>();
+        for (Analyzer analyzer : analyzers) {   
+            traverse(new Path(null, ".", analyzer), paths );
+        }
+
+    }
+
+    static class Path {
+        final Analyzer analyzer;
+        final String   pack;
+        final Path     previous;
+        
+        public Path(Path path, String pack, Analyzer analyzer) {
+            this.analyzer = analyzer;
+            this.previous = path;
+            this.pack = pack;
+        }
+
+        boolean visited(Analyzer analyzer) {
+            return this.analyzer == analyzer || previous == null || previous.visited(analyzer);
+        }
+    }
+
+    void traverse(Path path,
+            List<String> paths) {
+        
+        try {
+            for (String p : path.analyzer.getImports().keySet()) {
+                List<Analyzer> from = exports.get(p);
+                if (from != null) {
+                    for (Analyzer f : from) {
+                        
+           //             traverse(f, visited, paths);
+                    }
+                }
+            }
+        } finally {
+         //   visited.remove(analyzer);
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/JartoolSigner.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/JartoolSigner.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/JartoolSigner.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,134 @@
+package aQute.bnd.signing;
+
+import java.io.*;
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.reporter.*;
+
+/**
+ * Sign the jar file.
+ * 
+ * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:=' <keystore> ] [
+ * ';' 'sign-password:=' <pw> ] ( ',' ... )*
+ * 
+ * @author aqute
+ * 
+ */
+public class JartoolSigner implements Plugin, SignerPlugin {
+    String keystore;
+    String storetype;
+    String path = "jarsigner";
+    String storepass;
+    String keypass;
+    String sigFile;
+
+    public void setProperties(Map<String, String> map) {
+        if (map.containsKey("keystore"))
+            this.keystore = map.get("keystore");
+        if (map.containsKey("storetype"))
+            this.storetype = map.get("storetype");
+        if (map.containsKey("storepass"))
+            this.storepass = map.get("storepass");
+        if (map.containsKey("keypass"))
+            this.keypass = map.get("keypass");
+        if (map.containsKey("path"))
+            this.path = map.get("path");
+        if (map.containsKey("sigFile"))
+            this.sigFile = map.get("sigFile");
+
+    }
+
+    public void setReporter(Reporter processor) {
+    }
+
+    public void sign(Builder builder, String alias) throws Exception {
+        Jar jar = builder.getJar();
+        File tmp = File.createTempFile("signdjar", ".jar");
+        tmp.deleteOnExit();
+
+        jar.write(tmp);
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(path);
+        if (keystore != null) {
+            sb.append(" -keystore ");
+            sb.append(keystore);
+        }
+
+        if (storetype != null) {
+            sb.append(" -storetype ");
+            sb.append(storetype);
+        }
+
+        if (keypass != null) {
+            sb.append(" -keypass ");
+            sb.append(keypass);
+        }
+
+        if (storepass != null) {
+            sb.append(" -storepass ");
+            sb.append(storepass);
+        }
+
+        if (sigFile != null) {
+            sb.append(" -sigFile ");
+            sb.append(sigFile);
+        }
+
+        sb.append(" ");
+        sb.append(tmp.getAbsolutePath());
+        sb.append(" ");
+        sb.append(alias);
+
+        String cmd = sb.toString();
+
+        builder.trace(cmd);
+        final Process process = Runtime.getRuntime().exec(cmd);
+        Thread t = new Thread() {
+            public void run() {
+                try {
+                    collect(process.getInputStream());
+                    collect(process.getErrorStream());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        };
+        t.start();
+        process.waitFor();
+
+        if (process.exitValue() != 0) {
+            builder.error("Signing Jar: ");
+        } else {
+            // builder.trace(out);
+            // builder.trace(err);
+        }
+
+        Jar signed = new Jar(tmp);
+        builder.addClose(signed);
+
+        Map<String, Resource> dir = signed.getDirectories().get("META-INF");
+        for (String path : dir.keySet()) {
+            if (path.matches(".*\\.(DSA|RSA|SF|MF)$")) {
+                jar.putResource(path, dir.get(path));
+            }
+        }
+        jar.setDoNotTouchManifest();
+    }
+
+    String collect(InputStream in) throws Exception {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(in));
+        String line = rdr.readLine();
+        while (line != null) {
+            System.out.println(line);
+            sb.append(line);
+            line = rdr.readLine();
+        }
+        rdr.close();
+        in.close();
+        return sb.toString();
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/Signer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/Signer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/signing/Signer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,191 @@
+package aQute.bnd.signing;
+
+import java.io.*;
+import java.security.*;
+import java.security.KeyStore.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.lib.base64.*;
+import aQute.lib.osgi.*;
+
+/**
+ * This class is used with the aQute.lib.osgi package, it signs jars with DSA
+ * signature.
+ * 
+ * -sign: md5, sha1
+ */
+public class Signer extends Processor {
+    static Pattern  METAINFDIR   = Pattern.compile("META-INF/[^/]*");
+    String         digestNames[]    = new String[] { "MD5" };
+    File           keystoreFile  = new File("keystore");
+    String         password;
+    String         alias;
+
+    public void signJar(Jar jar) {
+        if (digestNames == null || digestNames.length == 0)
+            error("Need at least one digest algorithm name, none are specified");
+
+        if (keystoreFile == null || !keystoreFile.getAbsoluteFile().exists()) {
+            error("No such keystore file: " + keystoreFile);
+            return;
+        }
+
+        if (alias == null) {
+            error("Private key alias not set for signing");
+            return;
+        }
+
+        MessageDigest digestAlgorithms[] = new MessageDigest[digestNames.length];
+
+        getAlgorithms(digestNames, digestAlgorithms);
+
+        try {
+            Manifest manifest = jar.getManifest();
+            manifest.getMainAttributes().putValue("Signed-By", "Bnd");
+
+            // Create a new manifest that contains the
+            // Name parts with the specified digests
+
+            ByteArrayOutputStream o = new ByteArrayOutputStream();
+            manifest.write(o);
+            doManifest(jar, digestNames, digestAlgorithms, o);
+            o.flush();
+            byte newManifestBytes[] = o.toByteArray();
+            jar.putResource("META-INF/MANIFEST.MF", new EmbeddedResource(
+                    newManifestBytes, 0));
+
+            // Use the bytes from the new manifest to create
+            // a signature file
+
+            byte[] signatureFileBytes = doSignatureFile(digestNames,
+                    digestAlgorithms, newManifestBytes);
+            jar.putResource("META-INF/BND.SF", new EmbeddedResource(
+                    signatureFileBytes, 0));
+
+            // Now we must create an RSA signature
+            // this requires the private key from the keystore
+
+            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+
+            KeyStore.PrivateKeyEntry privateKeyEntry = null;
+
+            try {
+                java.io.FileInputStream keystoreInputStream = new java.io.FileInputStream(
+                        keystoreFile);
+                keystore.load(keystoreInputStream, password == null ? null
+                        : password.toCharArray());
+                keystoreInputStream.close();
+                privateKeyEntry =  (PrivateKeyEntry) keystore.getEntry(
+                        alias, new KeyStore.PasswordProtection(password
+                                .toCharArray()));
+            } catch (Exception e) {
+                error("No able to load the private key from the give keystore("+keystoreFile.getAbsolutePath()+") with alias "+alias+" : "
+                        + e);
+                return;
+            }
+            PrivateKey privateKey = privateKeyEntry.getPrivateKey();
+
+            Signature signature = Signature.getInstance("MD5withRSA");
+            signature.initSign(privateKey);
+
+            signature.update(signatureFileBytes);
+
+            signature.sign();
+
+            // TODO, place the SF in a PCKS#7 structure ...
+            // no standard class for this? The following
+            // is an idea but we will to have do ASN.1 BER
+            // encoding ...
+
+         
+            
+            ByteArrayOutputStream tmpStream = new ByteArrayOutputStream();
+            jar.putResource("META-INF/BND.RSA", new EmbeddedResource(tmpStream
+                    .toByteArray(), 0));
+        } catch (Exception e) {
+            error("During signing: " + e);
+        }
+    }
+
+    private byte[] doSignatureFile(String[] digestNames,
+            MessageDigest[] algorithms, byte[] manbytes) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(out);
+        ps.print("Signature-Version: 1.0\r\n");
+
+        for (int a = 0; a < algorithms.length; a++) {
+            if (algorithms[a] != null) {
+                byte[] digest = algorithms[a].digest(manbytes);
+                ps.print(digestNames[a] + "-Digest-Manifest: ");
+                ps.print(new Base64(digest));
+                ps.print("\r\n");
+            }
+        }
+        return out.toByteArray();
+    }
+
+    private void doManifest(Jar jar, String[] digestNames,
+            MessageDigest[] algorithms, OutputStream out) throws IOException {
+
+        for (Map.Entry<String,Resource> entry : jar.getResources().entrySet()) {
+            String name = entry.getKey();
+            if (!METAINFDIR.matcher(name).matches()) {
+                out.write("\r\n".getBytes());
+                out.write("Name: ".getBytes());
+                out.write(name.getBytes());
+                out.write("\r\n".getBytes());
+
+                digest(algorithms, entry.getValue());
+                for (int a = 0; a < algorithms.length; a++) {
+                    if (algorithms[a] != null) {
+                        byte[] digest = algorithms[a].digest();
+                        String header = digestNames[a] + "-Digest: "
+                                + new Base64(digest) + "\r\n";
+                        out.write(header.getBytes());
+                    }
+                }
+            }
+        }
+    }
+
+    private void digest(MessageDigest[] algorithms, Resource r)
+            throws IOException {
+        InputStream in = r.openInputStream();
+        byte[] data = new byte[1024];
+        int size = in.read(data);
+        while (size > 0) {
+            for (int a = 0; a < algorithms.length; a++) {
+                if (algorithms[a] != null) {
+                    algorithms[a].update(data, 0, size);
+                }
+            }
+            size = in.read(data);
+        }
+    }
+
+    private void getAlgorithms(String[] digestNames, MessageDigest[] algorithms) {
+        for (int i = 0; i < algorithms.length; i++) {
+            String name = digestNames[i];
+            try {
+                algorithms[i] = MessageDigest.getInstance(name);
+            } catch (NoSuchAlgorithmException e) {
+                error("Specified digest algorithm " + digestNames[i]
+                        + ", but not such algorithm was found: " + e);
+            }
+        }
+    }
+
+    public void setPassword(String string) {
+        password = string;
+    }
+
+    public void setKeystore(File keystore) {
+        this.keystoreFile = keystore;
+    }
+
+    public void setAlias(String string) {
+        this.alias = string;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/test/ProjectLauncher.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/test/ProjectLauncher.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/bnd/test/ProjectLauncher.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,366 @@
+package aQute.bnd.test;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import aQute.bnd.build.*;
+import aQute.lib.osgi.*;
+import aQute.libg.header.*;
+
+public class ProjectLauncher extends Processor {
+    final Project project;
+    static File   runtime;
+    File          report;
+    Process       process;
+    long          timeout = 60 * 60 * 1000;
+
+    public ProjectLauncher(Project project) {
+        super(project);
+        this.project = project;
+    }
+
+    /**
+     * Calculate the classpath. We include our own runtime.jar which includes
+     * the test framework and we include the first of the test frameworks
+     * specified.
+     */
+    public String[] getClasspath() {
+        try {
+            List<String> classpath = new ArrayList<String>();
+            classpath.add(getRuntime().getAbsolutePath());
+
+            for (Container c : project.getRunpath()) {
+                if (c.getType() != Container.TYPE.ERROR) {
+                    if (!c.getFile().getName().startsWith("ee."))
+                        classpath.add(c.getFile().getAbsolutePath());
+                } else {
+                    error("Invalid entry on the " + Constants.RUNPATH + ": "
+                            + c);
+                }
+            }
+            return classpath.toArray(new String[classpath.size()]);
+        } catch (Exception e) {
+            error("Calculating class path", e);
+        }
+        return null;
+    }
+
+    /**
+     * Extract the runtime on the file system so we can refer to it. in the
+     * remote VM.
+     * 
+     * @return
+     */
+    public static File getRuntime() {
+        if (runtime == null) {
+            try {
+                URL url = ProjectLauncher.class
+                        .getResource("aQute.runtime.jar");
+                if (url == null)
+                    throw new IllegalStateException(
+                            "Can not find my runtime.jar");
+
+                runtime = File.createTempFile("aQute.runtime", ".jar");
+                // runtime.deleteOnExit();
+                FileOutputStream out = new FileOutputStream(runtime);
+                InputStream in = url.openStream();
+                copy(in, out);
+                out.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return runtime;
+    }
+
+    public void doRunbundles(List<String> programArguments) throws Exception {
+        // we are going to use this for running, so we want to
+        // use the "best" version. For compile, we should
+        // use the lowest version.
+        Collection<Container> testbundles = project.getRunbundles();
+
+        for (Container c : testbundles) {
+            if (c.getError() != null)
+                error("Invalid bundle on " + Constants.RUNBUNDLES + " "
+                        + c.getError());
+            else {
+                // Do we need to build any sub projects?
+                if (c.getVersion() != null && c.getVersion().equals("project")) {
+                    if (c.getError() == null && c.getProject()!=null) {
+                        Project sub = c.getProject();
+
+                        sub.clear();
+                        File[] outputs = sub.build(false);
+                        for (File f : outputs) {
+                            programArguments.add("-bundle");
+                            programArguments.add(f.getAbsolutePath());
+                        }
+                        getInfo(sub);
+                    } else {
+                        error("Cannot find project " + c.getBundleSymbolicName() + " " + c.getError());
+                    }
+                } else {
+                    programArguments.add("-bundle");
+                    programArguments.add(c.getFile().getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    private void doRunpath(List<String> programArguments) throws Exception {
+        Collection<Container> testpath = project.getRunpath();
+        Container found = null;
+        for (Container c : testpath) {
+            if (c.getAttributes().containsKey("framework")) {
+                if (found != null) {
+                    warning("Specifying multiple framework classes on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("framework"));
+                found = c;
+            }
+            if (c.getAttributes().containsKey("factory")) {
+                if (found != null) {
+                    warning("Specifying multiple framework factories on the "
+                            + Constants.RUNPATH + "\n" + "Previous found: "
+                            + found.getProject() + " " + found.getAttributes()
+                            + "\n" + "Now found     : " + c.getProject() + " "
+                            + c.getAttributes());
+                }
+                programArguments.add("-framework");
+                programArguments.add(c.getAttributes().get("factory"));
+                found = c;
+            }
+            String exports = c.getAttributes().get("export");
+            if (exports != null) {
+                String parts[] = exports.split("\\s*,\\s*");
+                for (String p : parts) {
+                    programArguments.add("-export");
+                    programArguments.add(p);
+                }
+            }
+        }
+
+        doSystemPackages(programArguments);
+        doRunProperties(programArguments);
+    }
+
+    private void doRunProperties(List<String> programArguments) {
+        Map<String, String> properties = OSGiHeader
+                .parseProperties(getProperty(RUNPROPERTIES));
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+            programArguments.add("-set");
+            programArguments.add(entry.getKey());
+            programArguments.add(entry.getValue());
+        }
+    }
+
+    private void doSystemPackages(List<String> programArguments) {
+        Map<String, Map<String, String>> systemPackages = parseHeader(getProperty(RUNSYSTEMPACKAGES));
+        for (Map.Entry<String, Map<String, String>> entry : systemPackages
+                .entrySet()) {
+            programArguments.add("-export");
+            StringBuffer sb = new StringBuffer();
+            sb.append(entry.getKey());
+            printClause(entry.getValue(), null, sb);
+            programArguments.add(sb.toString());
+        }
+    }
+
+    private void doStorage(List<String> programArguments) throws Exception {
+        File tmp = new File(project.getTarget(), "fwtmp");
+        tmp.mkdirs();
+        tmp.deleteOnExit();
+
+        programArguments.add("-storage");
+        programArguments.add(tmp.getAbsolutePath());
+    }
+
+    /**
+     * Utility to copy a file from a resource.
+     * 
+     * @param in
+     * @param out
+     * @throws IOException
+     */
+    private static void copy(InputStream in, FileOutputStream out)
+            throws IOException {
+        byte buf[] = new byte[8192];
+        int size = in.read(buf);
+        while (size > 0) {
+            out.write(buf, 0, size);
+            size = in.read(buf);
+        }
+        in.close();
+    }
+
+    private Process launch(File[] targets) throws Exception {
+        List<String> arguments = newList();
+        List<String> vmArguments = newList();
+
+        vmArguments.add(getProperty("java", "java"));
+        doClasspath(vmArguments);
+
+        getArguments(targets, vmArguments, arguments);
+
+        vmArguments.add("aQute.junit.runtime.Target");
+        arguments.add("-report");
+        arguments.add(getTestreport().getAbsolutePath());
+
+        List<String> all = newList();
+        all.addAll(vmArguments);
+        all.addAll(arguments);
+
+        System.out.println("Cmd: " + all);
+
+        String[] cmdarray = all.toArray(new String[all.size()]);
+        if (getErrors().size() > 0)
+            return null;
+
+        return Runtime.getRuntime().exec(cmdarray, null, project.getBase());
+    }
+
+    /*
+     * static Pattern ARGUMENT = Pattern.compile("[-a-zA-Z0-9\\._]+"); private
+     * void doVMArguments(List<String> arguments) { Map<String,String> map =
+     * OSGiHeader.parseProperties( getProperty(RUNVM)); for ( String key :
+     * map.keySet() ) { if ( ARGUMENT.matcher(key).matches()) if (
+     * key.startsWith("-")) arguments.add(key); else arguments.add("-D" +
+     * key.trim() + "=" + map.get(key)); else warning("VM Argument is not a
+     * proper property key: " + key ); } }
+     */
+    private void doVMArguments(List<String> arguments) {
+        Map<String, String> map = OSGiHeader
+                .parseProperties(getProperty(RUNVM));
+        for (String key : map.keySet()) {
+            if (key.startsWith("-"))
+                arguments.add(key);
+            else
+                arguments.add("-D" + key.trim() + "=" + map.get(key));
+        }
+    }
+
+    private void doClasspath(List<String> arguments) {
+        Collection<String> cp = Arrays.asList(getClasspath());
+        if (!cp.isEmpty()) {
+            arguments.add("-classpath");
+            arguments.add(join(cp, File.pathSeparator));
+        }
+    }
+
+    public int run(File f) throws Exception {
+        process = launch(new File[] { f });
+
+        Thread killer = new Thread() {
+            public void run() {
+                process.destroy();
+            }
+        };
+        Runtime.getRuntime().addShutdownHook(killer);
+
+        Thread killer2 = new Thread("killer2") {
+            public void run() {
+                while (true) {
+                    try {
+                        Thread.sleep(timeout);
+                        System.out.println("EXITING BECAUSE OF OVERALL TIMEOUT: " + (timeout / 1000) + " secs");
+                        process.destroy();
+                    } catch (InterruptedException e) {
+                        return;
+                    }
+                }
+            }
+        };
+
+        Streamer sin = new Streamer(process.getInputStream(), System.out);
+        Streamer serr = new Streamer(process.getErrorStream(), System.out);
+        try {
+            killer2.start();
+            sin.start();
+            serr.start();
+            return process.waitFor();
+        } finally {
+            Runtime.getRuntime().removeShutdownHook(killer);
+            killer2.interrupt();
+            sin.join();
+            serr.join();
+        }
+    }
+
+    static class Streamer extends Thread {
+        final InputStream  in;
+        final OutputStream out;
+
+        Streamer(InputStream in, OutputStream out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        public void run() {
+            try {
+                int c;
+                while ((c = in.read()) > 0) {
+                    this.out.write(c);
+                }
+            } catch (IOException ioe) {
+                // Ignore
+            }
+        };
+
+    };
+
+    public File getTestreport() {
+        if (report != null)
+            return report;
+        String path = getProperty(Constants.TESTREPORT,
+                "${target}/test-report.xml");
+        report = getFile(path);
+        return report;
+
+    }
+
+    public void getArguments(List<String> vmArguments,
+            List<String> programArguments, boolean undertest) throws Exception {
+        File files[] = project.build(undertest);
+        getInfo(project);
+        if (files == null)
+            return;
+
+        getArguments(files, vmArguments, programArguments);
+    }
+
+    public void getArguments(File files[], List<String> vmArguments,
+            List<String> programArguments) throws Exception {
+        doVMArguments(vmArguments);
+        doStorage(programArguments);
+        doRunpath(programArguments);
+        doRunbundles(programArguments);
+        for (File file : files) {
+            programArguments.add("-target");
+            programArguments.add(file.getAbsolutePath());
+        }
+    }
+
+    public File getReport() {
+        return report;
+    }
+
+    public File setReport(String report) {
+        this.report = getFile(report);
+        return this.report;
+    }
+
+    public File setReport(File report) {
+        this.report = report;
+        return this.report;
+    }
+
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/base64/Base64.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/base64/Base64.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/base64/Base64.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,135 @@
+package aQute.lib.base64;
+
+import java.io.*;
+
+/*
+ * Base 64 converter.
+ * 
+ * TODO Implement string to byte[]
+ */
+public class Base64 {
+	byte[]				data;
+
+	static final String	alphabet	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	static byte[]		values		= new byte[128];
+
+	static {
+		for (int i = 0; i < values.length; i++) {
+			values[i] = -1;
+		}
+		// Create reverse index
+		for (int i = 0; i < alphabet.length(); i++) {
+			char c = alphabet.charAt(i);
+			values[c] = (byte) i;
+		}
+	}
+
+	public Base64(byte data[]) {
+		this.data = data;
+	}
+
+	public final static byte[] decodeBase64(String string) {
+		string = string.trim();
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+		int register = 0;
+		int i = 0;
+		int pads = 0;
+
+		byte test[] = new byte[3];
+
+		while (i < string.length()) {
+			char c = string.charAt(i);
+			if (c > 0x7F)
+				throw new IllegalArgumentException(
+						"Invalid base64 character in " + string
+								+ ", character value > 128 ");
+			
+			int v = 0;
+			if ( c == '=' ) {
+				pads++;
+			} else {
+				v = values[c];
+				if ( v < 0 )
+					throw new IllegalArgumentException(
+							"Invalid base64 character in " + string + ", " + c );
+			}					
+			register <<= 6;
+			register |= v;
+			test[2] = (byte) (register & 0xFF);
+			test[1] = (byte) ((register >> 8) & 0xFF);
+			test[0] = (byte) ((register >> 16) & 0xFF);
+
+			i++;
+
+			if ((i % 4) == 0) {
+				flush(out, register, pads);
+				register = 0;
+				pads = 0;
+			}
+		}
+		return out.toByteArray();
+	}
+
+	static private void flush(ByteArrayOutputStream out, int register, int pads) {
+		switch (pads) {
+		case 0:
+			out.write(0xFF & (register >> 16));
+			out.write(0xFF & (register >> 8));
+			out.write(0xFF & (register >> 0));
+			break;
+			
+		case 1:
+			out.write(0xFF & (register >> 16));
+			out.write(0xFF & (register >> 8));
+			break;
+			
+		case 2:
+			out.write(0xFF & (register >> 16));
+		}
+	}
+
+	public Base64(String s) {
+		data = decodeBase64(s);
+	}
+
+	public String toString() {
+		return encodeBase64(data);
+	}
+
+	public static String encodeBase64(byte data[]) {
+		StringBuffer sb = new StringBuffer();
+		int buf = 0;
+		int bits = 0;
+		int n = 0;
+
+		while (true) {
+			if (bits >= 6) {
+				bits -= 6;
+				int v = 0x3F & (buf >> bits);
+				sb.append(alphabet.charAt(v));
+			} else {
+				if (n >= data.length)
+					break;
+
+				buf <<= 8;
+				buf |= 0xFF & data[n++];
+				bits += 8;
+			}
+		}
+		if (bits != 0) // must be less than 7
+			sb.append(alphabet.charAt(0x3F & (buf << (6 - bits))));
+
+		int mod = 4 - (sb.length() % 4);
+		if (mod != 4) {
+			for (int i = 0; i < mod; i++)
+				sb.append('=');
+		}
+		return sb.toString();
+	}
+
+	public Object toData() {
+		return data;
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/deployer/FileRepo.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/deployer/FileRepo.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/deployer/FileRepo.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,230 @@
+package aQute.lib.deployer;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class FileRepo implements Plugin, RepositoryPlugin, Refreshable {
+    public static String LOCATION    = "location";
+    public static String READONLY    = "readonly";
+    public static String NAME    = "name";
+
+    File[]               EMPTY_FILES = new File[0];
+    File                 root;
+    boolean              canWrite    = true;
+    Pattern              REPO_FILE   = Pattern
+                                             .compile("([-a-zA-z0-9_\\.]+)-([0-9\\.]+|latest)\\.(jar|lib)");
+    Reporter             reporter;
+    boolean              dirty;
+    String name;
+    
+    public void setProperties(Map<String, String> map) {
+        String location = (String) map.get(LOCATION);
+        if (location == null)
+            throw new IllegalArgumentException(
+                    "Location muse be set on a FileRepo plugin");
+
+        root = new File(location);
+        if (!root.isDirectory())
+            throw new IllegalArgumentException(
+                    "Repository is not a valid directory " + root);
+
+        String readonly = (String) map.get(READONLY);
+        if (readonly != null && Boolean.valueOf(readonly).booleanValue())
+            canWrite = false;
+        
+        name = (String) map.get(NAME);
+    }
+
+    /**
+     * Get a list of URLs to bundles that are constrained by the bsn and
+     * versionRange.
+     */
+    public File[] get(String bsn, String versionRange)
+            throws MalformedURLException {
+
+        // If the version is set to project, we assume it is not
+        // for us. A project repo will then get it.
+        if (versionRange != null && versionRange.equals("project"))
+            return null;
+
+        //
+        // Check if the entry exists
+        //
+        File f = new File(root, bsn);
+        if (!f.isDirectory())
+            return null;
+
+        //
+        // The version range we are looking for can
+        // be null (for all) or a version range.
+        //
+        VersionRange range;
+        if (versionRange == null || versionRange.equals("latest")) {
+            range = new VersionRange("0");
+        } else
+            range = new VersionRange(versionRange);
+
+        //
+        // Iterator over all the versions for this BSN.
+        // Create a sorted map over the version as key
+        // and the file as URL as value. Only versions
+        // that match the desired range are included in
+        // this list.
+        //
+        File instances[] = f.listFiles();
+        SortedMap<Version, File> versions = new TreeMap<Version, File>();
+        for (int i = 0; i < instances.length; i++) {
+            Matcher m = REPO_FILE.matcher(instances[i].getName());
+            if (m.matches() && m.group(1).equals(bsn)) {
+                String versionString = m.group(2);
+                Version version;
+                if (versionString.equals("latest"))
+                    version = new Version(Integer.MAX_VALUE);
+                else
+                    version = new Version(versionString);
+
+                if (range.includes(version) || versionString.equals(versionRange))
+                    versions.put(version, instances[i]);
+            }
+        }
+        
+        File[] files = (File[]) versions.values().toArray(EMPTY_FILES);
+        if ("latest".equals(versionRange) && files.length > 0) {
+        	return new File[] { files[files.length - 1] };
+        }
+        return files;
+    }
+
+    public boolean canWrite() {
+        return canWrite;
+    }
+
+    public File put(Jar jar) throws Exception {
+        dirty = true;
+        
+        Manifest manifest = jar.getManifest();
+        if (manifest == null)
+            throw new IllegalArgumentException("No manifest in JAR: " + jar);
+
+        String bsn = manifest.getMainAttributes().getValue(
+                Analyzer.BUNDLE_SYMBOLICNAME);
+        if (bsn == null)
+            throw new IllegalArgumentException("No Bundle SymbolicName set");
+
+        Map<String, Map<String, String>> b = Processor.parseHeader(bsn, null);
+        if (b.size() != 1)
+            throw new IllegalArgumentException("Multiple bsn's specified " + b);
+
+        for (String key : b.keySet()) {
+            bsn = key;
+            if (!Verifier.SYMBOLICNAME.matcher(bsn).matches())
+                throw new IllegalArgumentException(
+                        "Bundle SymbolicName has wrong format: " + bsn);
+        }
+
+        String versionString = manifest.getMainAttributes().getValue(
+                Analyzer.BUNDLE_VERSION);
+        Version version;
+        if (versionString == null)
+            version = new Version();
+        else
+            version = new Version(versionString);
+
+        File dir = new File(root, bsn);
+        dir.mkdirs();
+        String fName = bsn + "-" + version.getMajor() + "."
+                + version.getMinor() + "." + version.getMicro() + ".jar";
+        File file = new File(dir, fName);
+
+        System.out.println("put" + file.getAbsolutePath() + " "
+                + file.lastModified() + " " + jar.lastModified());
+        if (file.lastModified() < jar.lastModified()) {
+            System.out.println("Updating " + fName);
+            jar.write(file);
+            // reportNewer(file.lastModified(), jar);
+        } else {
+            reporter.progress("Did not update " + jar
+                    + " because repo has a newer version");
+            System.out.println("NOT Updating " + fName + " (repo is newer)");
+        }
+
+        file = new File(dir, bsn + "-latest.jar");
+        if (file.isFile() && file.lastModified() < jar.lastModified()) {
+            jar.write(file);
+        }
+        return file;
+    }
+
+    public void setLocation(String string) {
+        root = new File(string);
+        if (!root.isDirectory())
+            throw new IllegalArgumentException("Invalid repository directory");
+    }
+
+    public void setReporter(Reporter reporter) {
+        this.reporter = reporter;
+    }
+
+    public List<String> list(String regex) {
+
+        Instruction pattern = null;
+        if (regex != null)
+            pattern = Instruction.getPattern(regex);
+
+        String list[] = root.list();
+        List<String> result = new ArrayList<String>();
+        for (String f : list) {
+            if (pattern == null || pattern.matches(f))
+                result.add(f);
+        }
+        return result;
+    }
+
+    public List<Version> versions(String bsn) {
+        File dir = new File(root, bsn);
+        if (dir.isDirectory()) {
+            String versions[] = dir.list();
+            List<Version> list = new ArrayList<Version>();
+            for (String v : versions) {
+                Matcher m = REPO_FILE.matcher(v);
+                if (m.matches()) {
+                    list.add(new Version(m.group(2)));
+                }
+            }
+            return list;
+        }
+        return null;
+    }
+
+    public String toString() {
+        return String
+                .format("%-40s r/w=%s", root.getAbsolutePath(), canWrite());
+    }
+
+    public File getRoot() {
+        return root;
+    }
+
+    public boolean refresh() {
+        if ( dirty ) {
+            dirty = false;
+            return true;
+        } else 
+            return false;
+    }
+
+	public String getName() {
+		if (name == null) {
+			return toString();
+		}
+		return name;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/jardiff/Diff.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/jardiff/Diff.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/jardiff/Diff.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,152 @@
+package aQute.lib.jardiff;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.lib.osgi.*;
+
+public class Diff {
+
+    /**
+     * Compare two JAR files with each other.
+     * 
+     * @param a
+     * @param b
+     * @param strict
+     * @return
+     * @throws IOException
+     */
+    public Map<String, Object> diff(Jar a, Jar b, boolean strict)
+            throws IOException {
+        Map<String, Object> different = new TreeMap<String, Object>();
+        compareManifest(different, a.getManifest(), b.getManifest(), strict);
+        diff(different, a.getResources().keySet(), b.getResources().keySet());
+
+        Set<String> shared = new HashSet<String>(a.getResources().keySet());
+        shared.retainAll(b.getResources().keySet());
+
+        for (String path : a.getResources().keySet()) {
+            Resource ra = a.getResource(path);
+            Resource rb = a.getResource(path);
+            if (rb != null) {
+                if (ra.getClass() != rb.getClass()) {
+                    different.put(path, "Different types: "
+                            + a.getClass().getName() + " : "
+                            + b.getClass().getName());
+                } else {
+                    if (path.endsWith(".jar")) {
+                        Jar aa = new Jar(path, ra.openInputStream());
+                        Jar bb = new Jar(path, rb.openInputStream());
+                        try {
+                            Map<String, Object> result = diff(aa, bb, strict);
+                            if (!result.isEmpty())
+                                different.put(path, result);
+                        } finally {
+                            aa.close();
+                            bb.close();
+                        }
+                    } else {
+                        String cmp = diff(ra.openInputStream(), rb
+                                .openInputStream());
+                        if (cmp != null)
+                            different.put(path, cmp);
+                    }
+                }
+            }
+        }
+        return different;
+    }
+
+    String diff(InputStream a, InputStream b) throws IOException {
+        int n = 0;
+        int binary = 0;
+        StringBuffer sb = new StringBuffer();
+        while (true) {
+            int ac = a.read();
+            int bc = b.read();
+            if (ac < 0) {
+                if (bc < 0)
+                    return null;
+
+                return "a is smaller";
+            } else if (bc < 0) {
+                return "b is smaller";
+            }
+
+            if (ac != bc) {
+                String s = "Difference at pos: " + n;
+                if (binary == 0 && sb.length() > 5) {
+                    s = s + "Context: " + sb.substring(sb.length() - 5);
+                }
+            }
+
+            if (ac >= ' ' && ac <= '~')
+                sb.append((char) ac);
+            else
+                binary++;
+
+            n++;
+        }
+    }
+
+    void diff(Map<String, Object> different, Set<?> a, Set<?> b) {
+        Set<Object> onlyInA = new HashSet<Object>(a);
+        onlyInA.removeAll(b);
+        Set<Object> onlyInB = new HashSet<Object>(b);
+        onlyInB.removeAll(a);
+
+        for (Object element : onlyInA) {
+            different.put(element.toString(), "a");
+        }
+        for (Object element : onlyInB) {
+            different.put(element.toString(), "b");
+        }
+    }
+
+    public void compareManifest(Map<String, Object> different, Manifest a,
+            Manifest b, boolean strict) {
+        if (a == null || b == null) {
+            different.put("Manifest null", (a == null ? "a=null" : "a exists")
+                    + " " + (b == null ? "b=null" : "b exists"));
+            return;
+        }
+
+        Attributes attrs = a.getMainAttributes();
+        Attributes bttrs = b.getMainAttributes();
+        diff(different, attrs.keySet(), bttrs.keySet());
+        for (Object element : attrs.keySet()) {
+            Attributes.Name name = (Attributes.Name) element;
+            String av = attrs.getValue(name);
+            String bv = bttrs.getValue(name);
+            if (bv != null) {
+                if (!av.equals(bv))
+                    different.put(name.toString(), "M:" + name + ":" + av
+                            + "!=" + bv);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void print(PrintStream pout, Map<String, Object> map, int indent) {
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            for (int j = 0; j < indent; j++) {
+                pout.print(" ");
+            }
+            String key = entry.getKey();
+            pout.print(key);
+            for (int j = 0; j < 70 - indent - key.length(); j++) {
+                pout.print(" ");
+            }
+            if (entry.getValue() instanceof Map) {
+                pout.println();
+                print(pout, (Map<String, Object>) entry.getValue(), indent + 1);
+            } else
+                pout.println(entry.getValue());
+        }
+    }
+
+    public void close() {
+
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/About.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/About.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/About.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,46 @@
+package aQute.lib.osgi;
+
+/**
+ * This package contains a number of classes that assists by analyzing JARs and
+ * constructing bundles.
+ * 
+ * The Analyzer class can be used to analyze an existing bundle and can create a
+ * manifest specification from proposed (wildcard) Export-Package,
+ * Bundle-Includes, and Import-Package headers.
+ * 
+ * The Builder class can use the headers to construct a JAR from the classpath.
+ * 
+ * The Verifier class can take an existing JAR and verify that all headers are
+ * correctly set. It will verify the syntax of the headers, match it against the
+ * proper contents, and verify imports and exports.
+ * 
+ * A number of utility classes are available.
+ * 
+ * Jar, provides an abstraction of a Jar file. It has constructors for creating
+ * a Jar from a stream, a directory, or a jar file. A Jar, keeps a collection
+ * Resource's. There are Resource implementations for File, from ZipFile, or from
+ * a stream (which copies the data). The Jar tries to minimize the work during
+ * build up so that it is cheap to use. The Resource's can be used to iterate 
+ * over the names and later read the resources when needed.
+ * 
+ * Clazz, provides a parser for the class files. This will be used to define the
+ * imports and exports.
+ * 
+ * A key component in this library is the Map. Headers are translated to Maps of Maps. OSGi
+ * header syntax is like:
+ * <pre>
+ * 	  header = clause ( ',' clause ) *
+ *    clause = file ( ';' file ) * ( parameter ) *
+ *    param  = attr '=' value | directive ':=' value
+ * </pre>
+ * These headers are translated to a Map that contains all headers (the order is
+ * maintained). Each additional file in a header definition will have its own
+ * entry (only native code does not work this way). The clause is represented
+ * as another map. The ':' of directives is considered part of the name. This
+ * allows attributes and directives to be maintained in the clause map. 
+ * 
+ * @version $Revision: 1.2 $
+ */
+public class About {
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/AbstractResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/AbstractResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/AbstractResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,50 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public abstract class AbstractResource implements Resource {
+    String extra;
+    byte[] calculated;
+    long   lastModified;
+
+    protected AbstractResource(long modified) {
+        lastModified = modified;
+    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return new ByteArrayInputStream(getLocalBytes());
+    }
+
+    private byte[] getLocalBytes() throws IOException {
+        try {
+            if (calculated != null)
+                return calculated;
+
+            return calculated = getBytes();
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            IOException ee = new IOException("Opening resource");
+            ee.initCause(e);
+            throw ee;
+        }
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+
+    public void write(OutputStream out) throws IOException {
+        out.write(getLocalBytes());
+    }
+
+    abstract protected byte[] getBytes() throws Exception;
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Analyzer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Analyzer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Analyzer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1604 @@
+package aQute.lib.osgi;
+
+/**
+ * This class can calculate the required headers for a (potential) JAR file. It
+ * analyzes a directory or JAR for the packages that are contained and that are
+ * referred to by the bytecodes. The user can the use regular expressions to
+ * define the attributes and directives. The matching is not fully regex for
+ * convenience. A * and ? get a . prefixed and dots are escaped.
+ * 
+ * <pre>
+ *                                                             			*;auto=true				any		
+ *                                                             			org.acme.*;auto=true    org.acme.xyz
+ *                                                             			org.[abc]*;auto=true    org.acme.xyz
+ * </pre>
+ * 
+ * Additional, the package instruction can start with a '=' or a '!'. The '!'
+ * indicates negation. Any matching package is removed. The '=' is literal, the
+ * expression will be copied verbatim and no matching will take place.
+ * 
+ * Any headers in the given properties are used in the output properties.
+ */
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.jar.Attributes.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+
+public class Analyzer extends Processor {
+
+    static Pattern                         doNotCopy      = Pattern
+                                                                  .compile("CVS|.svn");
+    static String                          version;
+    static Pattern                         versionPattern = Pattern
+                                                                  .compile("(\\d+\\.\\d+)\\.\\d+.*");
+    final Map<String, Map<String, String>> contained      = newHashMap();                            // package
+    final Map<String, Map<String, String>> referred       = newHashMap();                            // package
+    final Map<String, Set<String>>         uses           = newHashMap();                            // package
+    Map<String, Clazz>                     classspace;
+    Map<String, Map<String, String>>       exports;
+    Map<String, Map<String, String>>       imports;
+    Map<String, Map<String, String>>       bundleClasspath;                                          // Bundle
+    final Map<String, Map<String, String>> ignored        = newHashMap();                            // Ignored
+    // packages
+    Jar                                    dot;
+    Map<String, Map<String, String>>       classpathExports;
+
+    String                                 activator;
+
+    final List<Jar>                        classpath      = newList();
+
+    static Properties                      bndInfo;
+
+    boolean                                analyzed;
+    String                                 bsn;
+
+    public Analyzer(Processor parent) {
+        super(parent);
+    }
+
+    public Analyzer() {
+    }
+
+    /**
+     * Specifically for Maven
+     * 
+     * @param properties
+     *            the properties
+     */
+
+    public static Properties getManifest(File dirOrJar) throws IOException {
+        Analyzer analyzer = new Analyzer();
+        analyzer.setJar(dirOrJar);
+        Properties properties = new Properties();
+        properties.put(IMPORT_PACKAGE, "*");
+        properties.put(EXPORT_PACKAGE, "*");
+        analyzer.setProperties(properties);
+        Manifest m = analyzer.calcManifest();
+        Properties result = new Properties();
+        for (Iterator<Object> i = m.getMainAttributes().keySet().iterator(); i
+                .hasNext();) {
+            Attributes.Name name = (Attributes.Name) i.next();
+            result.put(name.toString(), m.getMainAttributes().getValue(name));
+        }
+        return result;
+    }
+
+    /**
+     * Calcualtes the data structures for generating a manifest.
+     * 
+     * @throws IOException
+     */
+    public void analyze() throws IOException {
+        if (!analyzed) {
+            analyzed = true;
+            classpathExports = newHashMap();
+            activator = getProperty(BUNDLE_ACTIVATOR);
+            bundleClasspath = parseHeader(getProperty(BUNDLE_CLASSPATH));
+
+            analyzeClasspath();
+
+            classspace = analyzeBundleClasspath(dot, bundleClasspath,
+                    contained, referred, uses);
+
+            for (AnalyzerPlugin plugin : getPlugins(AnalyzerPlugin.class)) {
+                if (plugin instanceof AnalyzerPlugin) {
+                    AnalyzerPlugin analyzer = (AnalyzerPlugin) plugin;
+                    try {
+                        boolean reanalyze = analyzer.analyzeJar(this);
+                        if (reanalyze)
+                            classspace = analyzeBundleClasspath(dot,
+                                    bundleClasspath, contained, referred, uses);
+                    } catch (Exception e) {
+                        error("Plugin Analyzer " + analyzer
+                                + " throws exception " + e);
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+            if (activator != null) {
+                // Add the package of the activator to the set
+                // of referred classes. This must be done before we remove
+                // contained set.
+                int n = activator.lastIndexOf('.');
+                if (n > 0) {
+                    referred.put(activator.substring(0, n),
+                            new LinkedHashMap<String, String>());
+                }
+            }
+
+            referred.keySet().removeAll(contained.keySet());
+            if (referred.containsKey(".")) {
+                error("The default package '.' is not permitted by the Import-Package syntax. \n"
+                        + " This can be caused by compile errors in Eclipse because Eclipse creates \n"
+                        + "valid class files regardless of compile errors.\n"
+                        + "The following package(s) import from the default package "
+                        + getUsedBy("."));
+            }
+
+            Map<String, Map<String, String>> exportInstructions = parseHeader(getProperty(EXPORT_PACKAGE));
+            Map<String, Map<String, String>> additionalExportInstructions = parseHeader(getProperty(EXPORT_CONTENTS));
+            exportInstructions.putAll(additionalExportInstructions);
+            Map<String, Map<String, String>> importInstructions = parseHeader(getImportPackages());
+            Map<String, Map<String, String>> dynamicImports = parseHeader(getProperty(DYNAMICIMPORT_PACKAGE));
+
+            if (dynamicImports != null) {
+                // Remove any dynamic imports from the referred set.
+                referred.keySet().removeAll(dynamicImports.keySet());
+            }
+
+            Map<String, Map<String, String>> superfluous = newHashMap();
+            // Tricky!
+            for (Iterator<String> i = exportInstructions.keySet().iterator(); i
+                    .hasNext();) {
+                String instr = i.next();
+                if (!instr.startsWith("!"))
+                    superfluous.put(instr, exportInstructions.get(instr));
+            }
+
+            exports = merge("export-package", exportInstructions, contained,
+                    superfluous.keySet(), null);
+
+            // disallow export of default package
+            exports.remove(".");
+
+            for (Iterator<Map.Entry<String, Map<String, String>>> i = superfluous
+                    .entrySet().iterator(); i.hasNext();) {
+                // It is possible to mention metadata directories in the export
+                // explicitly, they are then exported and removed from the
+                // warnings. Note that normally metadata directories are not
+                // exported.
+                Map.Entry<String, Map<String, String>> entry = i.next();
+                String pack = entry.getKey();
+                if (isDuplicate(pack))
+                    i.remove();
+                else if (isMetaData(pack)) {
+                    exports.put(pack, entry.getValue());
+                    i.remove();
+                }
+            }
+
+            if (!superfluous.isEmpty()) {
+                warning("Superfluous export-package instructions: "
+                        + superfluous.keySet());
+            }
+
+            // Add all exports that do not have an -noimport: directive
+            // to the imports.
+            Map<String, Map<String, String>> referredAndExported = newMap(referred);
+            referredAndExported.putAll(addExportsToImports(exports));
+
+            // match the imports to the referred and exported packages,
+            // merge the info for matching packages
+            Set<String> extra = new TreeSet<String>(importInstructions.keySet());
+            imports = merge("import-package", importInstructions,
+                    referredAndExported, extra, ignored);
+
+            // Instructions that have not been used could be superfluous
+            // or if they do not contain wildcards, should be added
+            // as extra imports, the user knows best.
+            for (Iterator<String> i = extra.iterator(); i.hasNext();) {
+                String p = i.next();
+                if (p.startsWith("!") || p.indexOf('*') >= 0
+                        || p.indexOf('?') >= 0 || p.indexOf('[') >= 0) {
+                    if (!isResourceOnly())
+                        warning("Did not find matching referal for " + p);
+                } else {
+                    Map<String, String> map = importInstructions.get(p);
+                    imports.put(p, map);
+                }
+            }
+
+            // See what information we can find to augment the
+            // imports. I.e. look on the classpath
+            augmentImports();
+
+            // Add the uses clause to the exports
+            doUses(exports, uses, imports);
+        }
+    }
+
+    /**
+     * Copy the input collection into an output set but skip names that have
+     * been marked as duplicates or are optional.
+     * 
+     * @param superfluous
+     * @return
+     */
+    Set<Instruction> removeMarkedDuplicates(Collection<Instruction> superfluous) {
+        Set<Instruction> result = new HashSet<Instruction>();
+        for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
+            Instruction instr = (Instruction) i.next();
+            if (!isDuplicate(instr.getPattern()) && !instr.isOptional())
+                result.add(instr);
+        }
+        return result;
+    }
+
+    /**
+     * Analyzer has an empty default but the builder has a * as default.
+     * 
+     * @return
+     */
+    protected String getImportPackages() {
+        return getProperty(IMPORT_PACKAGE);
+    }
+
+    /**
+     * 
+     * @return
+     */
+    boolean isResourceOnly() {
+        return getProperty(RESOURCEONLY, "false").equalsIgnoreCase("true");
+    }
+
+    /**
+     * Answer the list of packages that use the given package.
+     */
+    Set<String> getUsedBy(String pack) {
+        Set<String> set = newSet();
+        for (Iterator<Map.Entry<String, Set<String>>> i = uses.entrySet()
+                .iterator(); i.hasNext();) {
+            Map.Entry<String, Set<String>> entry = i.next();
+            Set<String> used = entry.getValue();
+            if (used.contains(pack))
+                set.add(entry.getKey());
+        }
+        return set;
+    }
+
+    /**
+     * One of the main workhorses of this class. This will analyze the current
+     * setp and calculate a new manifest according to this setup. This method
+     * will also set the manifest on the main jar dot
+     * 
+     * @return
+     * @throws IOException
+     */
+    public Manifest calcManifest() throws IOException {
+        analyze();
+        Manifest manifest = new Manifest();
+        Attributes main = manifest.getMainAttributes();
+
+        main.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        main.putValue(BUNDLE_MANIFESTVERSION, "2");
+
+        boolean noExtraHeaders = "true"
+                .equalsIgnoreCase(getProperty(NOEXTRAHEADERS));
+
+        if (!noExtraHeaders) {
+            main.putValue(CREATED_BY, System.getProperty("java.version") + " ("
+                    + System.getProperty("java.vendor") + ")");
+            main.putValue(TOOL, "Bnd-" + getVersion());
+            main.putValue(BND_LASTMODIFIED, "" + System.currentTimeMillis());
+        }
+        String exportHeader = printClauses(exports,
+                "uses:|include:|exclude:|mandatory:|" + IMPORT_DIRECTIVE, true);
+
+        if (exportHeader.length() > 0)
+            main.putValue(EXPORT_PACKAGE, exportHeader);
+        else
+            main.remove(EXPORT_PACKAGE);
+
+        Map<String, Map<String, String>> temp = removeKeys(imports, "java.");
+        if (!temp.isEmpty()) {
+            main.putValue(IMPORT_PACKAGE, printClauses(temp, "resolution:"));
+        } else {
+            main.remove(IMPORT_PACKAGE);
+        }
+
+        temp = newMap(contained);
+        temp.keySet().removeAll(exports.keySet());
+
+        if (!temp.isEmpty())
+            main.putValue(PRIVATE_PACKAGE, printClauses(temp, ""));
+        else
+            main.remove(PRIVATE_PACKAGE);
+
+        if (!ignored.isEmpty()) {
+            main.putValue(IGNORE_PACKAGE, printClauses(ignored, ""));
+        } else {
+            main.remove(IGNORE_PACKAGE);
+        }
+
+        if (bundleClasspath != null && !bundleClasspath.isEmpty())
+            main.putValue(BUNDLE_CLASSPATH, printClauses(bundleClasspath, ""));
+        else
+            main.remove(BUNDLE_CLASSPATH);
+
+        for (Enumeration<?> h = getProperties().propertyNames(); h
+                .hasMoreElements();) {
+            String header = (String) h.nextElement();
+            if (header.trim().length() == 0) {
+                warning("Empty property set with value: "
+                        + getProperties().getProperty(header));
+                continue;
+            }
+            
+            if ( isMissingPlugin(header.trim())){
+                error("Missing plugin for command %s", header );
+            }             
+            if (!Character.isUpperCase(header.charAt(0))) {
+                if (header.charAt(0) == '@')
+                    doNameSection(manifest, header);
+                continue;
+            }
+
+            if (header.equals(BUNDLE_CLASSPATH)
+                    || header.equals(EXPORT_PACKAGE)
+                    || header.equals(IMPORT_PACKAGE))
+                continue;
+
+            if (header.equalsIgnoreCase("Name")) {
+                error("Your bnd file contains a header called 'Name'. This interferes with the manifest name section.");
+                continue;
+            }
+
+            if (Verifier.HEADER_PATTERN.matcher(header).matches()) {
+                String value = getProperty(header);
+                if (value != null && main.getValue(header) == null) {
+                    if (value.trim().length() == 0)
+                        main.remove(header);
+                    else if (value.trim().equals("<<EMPTY>>"))
+                        main.putValue(header, "");
+                    else
+                        main.putValue(header, value);
+                }
+            } else {
+                // TODO should we report?                
+            }
+        }
+
+        //
+        // Calculate the bundle symbolic name if it is
+        // not set.
+        // 1. set
+        // 2. name of properties file (must be != bnd.bnd)
+        // 3. name of directory, which is usualy project name
+        //
+        String bsn = getBsn();
+        if (main.getValue(BUNDLE_SYMBOLICNAME) == null) {
+            main.putValue(BUNDLE_SYMBOLICNAME, bsn);
+        }
+
+        //
+        // Use the same name for the bundle name as BSN when
+        // the bundle name is not set
+        //
+        if (main.getValue(BUNDLE_NAME) == null) {
+            main.putValue(BUNDLE_NAME, bsn);
+        }
+
+        if (main.getValue(BUNDLE_VERSION) == null)
+            main.putValue(BUNDLE_VERSION, "0");
+
+        // Copy old values into new manifest, when they
+        // exist in the old one, but not in the new one
+        merge(manifest, dot.getManifest());
+
+        // Remove all the headers mentioned in -removeheaders
+        Map<String, Map<String, String>> removes = parseHeader(getProperty(REMOVE_HEADERS));
+        for (Iterator<String> i = removes.keySet().iterator(); i.hasNext();) {
+            String header = i.next();
+            for (Iterator<Object> j = main.keySet().iterator(); j.hasNext();) {
+                Attributes.Name attr = (Attributes.Name) j.next();
+                if (attr.toString().matches(header)) {
+                    j.remove();
+                    progress("Removing header: " + header);
+                }
+            }
+        }
+
+        dot.setManifest(manifest);
+        return manifest;
+    }
+
+    /**
+     * This method is called when the header starts with a @, signifying a name
+     * section header. The name part is defined by replacing all the @ signs to
+     * a /, removing the first and the last, and using the last part as header
+     * name:
+     * 
+     * <pre>
+     * &#064;org at osgi@service at event@Implementation-Title
+     * </pre>
+     * 
+     * This will be the header Implementation-Title in the
+     * org/osgi/service/event named section.
+     * 
+     * @param manifest
+     * @param header
+     */
+    private void doNameSection(Manifest manifest, String header) {
+        String path = header.replace('@', '/');
+        int n = path.lastIndexOf('/');
+        // Must succeed because we start with @
+        String name = path.substring(n + 1);
+        // Skip first /
+        path = path.substring(1, n);
+        if (name.length() != 0 && path.length() != 0) {
+            Attributes attrs = manifest.getAttributes(path);
+            if (attrs == null) {
+                attrs = new Attributes();
+                manifest.getEntries().put(path, attrs);
+            }
+            attrs.putValue(name, getProperty(header));
+        } else {
+            warning(
+                    "Invalid header (starts with @ but does not seem to be for the Name section): %s",
+                    header);
+        }
+    }
+
+    /**
+     * Clear the key part of a header. I.e. remove everything from the first ';'
+     * 
+     * @param value
+     * @return
+     */
+    public String getBsn() {
+        String value = getProperty(BUNDLE_SYMBOLICNAME);
+        if (value == null) {
+            if (getPropertiesFile() != null)
+                value = getPropertiesFile().getName();
+
+            if (value == null || value.equals("bnd.bnd"))
+                value = getBase().getName();
+            else if (value.endsWith(".bnd"))
+                value = value.substring(0, value.length() - 4);
+        }
+
+        if (value == null)
+            return "untitled";
+
+        int n = value.indexOf(';');
+        if (n > 0)
+            value = value.substring(0, n);
+        return value.trim();
+    }
+
+    public String _bsn(String args[]) {
+        return getBsn();
+    }
+
+    /**
+     * Calculate an export header solely based on the contents of a JAR file
+     * 
+     * @param bundle
+     *            The jar file to analyze
+     * @return
+     */
+    public String calculateExportsFromContents(Jar bundle) {
+        String ddel = "";
+        StringBuffer sb = new StringBuffer();
+        Map<String, Map<String, Resource>> map = bundle.getDirectories();
+        for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
+            String directory = (String) i.next();
+            if (directory.equals("META-INF")
+                    || directory.startsWith("META-INF/"))
+                continue;
+            if (directory.equals("OSGI-OPT")
+                    || directory.startsWith("OSGI-OPT/"))
+                continue;
+            if (directory.equals("/"))
+                continue;
+
+            if (directory.endsWith("/"))
+                directory = directory.substring(0, directory.length() - 1);
+
+            directory = directory.replace('/', '.');
+            sb.append(ddel);
+            sb.append(directory);
+            ddel = ",";
+        }
+        return sb.toString();
+    }
+
+    public Map<String, Map<String, String>> getBundleClasspath() {
+        return bundleClasspath;
+    }
+
+    public Map<String, Map<String, String>> getContained() {
+        return contained;
+    }
+
+    public Map<String, Map<String, String>> getExports() {
+        return exports;
+    }
+
+    public Map<String, Map<String, String>> getImports() {
+        return imports;
+    }
+
+    public Jar getJar() {
+        return dot;
+    }
+
+    public Map<String, Map<String, String>> getReferred() {
+        return referred;
+    }
+
+    /**
+     * Return the set of unreachable code depending on exports and the bundle
+     * activator.
+     * 
+     * @return
+     */
+    public Set<String> getUnreachable() {
+        Set<String> unreachable = new HashSet<String>(uses.keySet()); // all
+        for (Iterator<String> r = exports.keySet().iterator(); r.hasNext();) {
+            String packageName = r.next();
+            removeTransitive(packageName, unreachable);
+        }
+        if (activator != null) {
+            String pack = activator.substring(0, activator.lastIndexOf('.'));
+            removeTransitive(pack, unreachable);
+        }
+        return unreachable;
+    }
+
+    public Map<String, Set<String>> getUses() {
+        return uses;
+    }
+
+    /**
+     * Get the version from the manifest, a lot of work!
+     * 
+     * @return version or unknown.
+     */
+    public String getVersion() {
+        return getBndInfo("version", "<unknown version>");
+    }
+
+    public long getBndLastModified() {
+        String time = getBndInfo("modified", "0");
+        try {
+            return Long.parseLong(time);
+        } catch (Exception e) {
+        }
+        return 0;
+    }
+
+    public String getBndInfo(String key, String defaultValue) {
+        if (bndInfo == null) {
+            bndInfo = new Properties();
+            try {
+                InputStream in = Analyzer.class.getResourceAsStream("bnd.info");
+                if (in != null) {
+                    bndInfo.load(in);
+                    in.close();
+                }
+            } catch (IOException ioe) {
+                warning("Could not read bnd.info in "
+                        + Analyzer.class.getPackage() + ioe);
+            }
+        }
+        return bndInfo.getProperty(key, defaultValue);
+    }
+
+    /**
+     * Merge the existing manifest with the instructions.
+     * 
+     * @param manifest
+     *            The manifest to merge with
+     * @throws IOException
+     */
+    public void mergeManifest(Manifest manifest) throws IOException {
+        if (manifest != null) {
+            Attributes attributes = manifest.getMainAttributes();
+            for (Iterator<Object> i = attributes.keySet().iterator(); i
+                    .hasNext();) {
+                Name name = (Name) i.next();
+                String key = name.toString();
+                // Dont want instructions
+                if (key.startsWith("-"))
+                    continue;
+
+                if (getProperty(key) == null)
+                    setProperty(key, (String) attributes.get(name));
+            }
+        }
+    }
+
+    public void setBase(File file) {
+        super.setBase(file);
+        getProperties().put("project.dir", getBase().getAbsolutePath());
+    }
+
+    /**
+     * Set the classpath for this analyzer by file.
+     * 
+     * @param classpath
+     * @throws IOException
+     */
+    public void setClasspath(File[] classpath) throws IOException {
+        List<Jar> list = new ArrayList<Jar>();
+        for (int i = 0; i < classpath.length; i++) {
+            if (classpath[i].exists()) {
+                Jar current = new Jar(classpath[i]);
+                list.add(current);
+            } else {
+                error("Missing file on classpath: " + classpath[i]);
+            }
+        }
+        for (Iterator<Jar> i = list.iterator(); i.hasNext();) {
+            addClasspath(i.next());
+        }
+    }
+
+    public void setClasspath(Jar[] classpath) {
+        for (int i = 0; i < classpath.length; i++) {
+            addClasspath(classpath[i]);
+        }
+    }
+
+    public void setClasspath(String[] classpath) {
+        for (int i = 0; i < classpath.length; i++) {
+            Jar jar = getJarFromName(classpath[i], " setting classpath");
+            if (jar != null)
+                addClasspath(jar);
+        }
+    }
+
+    /**
+     * Set the JAR file we are going to work in. This will read the JAR in
+     * memory.
+     * 
+     * @param jar
+     * @return
+     * @throws IOException
+     */
+    public Jar setJar(File jar) throws IOException {
+        Jar jarx = new Jar(jar);
+        addClose(jarx);
+        return setJar(jarx);
+    }
+
+    /**
+     * Set the JAR directly we are going to work on.
+     * 
+     * @param jar
+     * @return
+     */
+    public Jar setJar(Jar jar) {
+        this.dot = jar;
+        return jar;
+    }
+
+    protected void begin() {
+        super.begin();
+
+        updateModified(getBndLastModified(), "bnd last modified");
+        String doNotCopy = getProperty(DONOTCOPY);
+        if (doNotCopy != null)
+            Analyzer.doNotCopy = Pattern.compile(doNotCopy);
+
+        verifyManifestHeadersCase(getProperties());
+    }
+
+    /**
+     * Check if the given class or interface name is contained in the jar.
+     * 
+     * @param interfaceName
+     * @return
+     */
+    public boolean checkClass(String interfaceName) {
+        String path = interfaceName.replace('.', '/') + ".class";
+        if (classspace.containsKey(path))
+            return true;
+
+        String pack = interfaceName;
+        int n = pack.lastIndexOf('.');
+        if (n > 0)
+            pack = pack.substring(0, n);
+        else
+            pack = ".";
+
+        return imports.containsKey(pack);
+    }
+
+    /**
+     * Try to get a Jar from a file name/path or a url, or in last resort from
+     * the classpath name part of their files.
+     * 
+     * @param name
+     *            URL or filename relative to the base
+     * @param from
+     *            Message identifying the caller for errors
+     * @return null or a Jar with the contents for the name
+     */
+    Jar getJarFromName(String name, String from) {
+        File file = new File(name);
+        if (!file.isAbsolute())
+            file = new File(getBase(), name);
+
+        if (file.exists())
+            try {
+                Jar jar = new Jar(file);
+                addClose(jar);
+                return jar;
+            } catch (Exception e) {
+                error("Exception in parsing jar file for " + from + ": " + name
+                        + " " + e);
+            }
+        // It is not a file ...
+        try {
+            // Lets try a URL
+            URL url = new URL(name);
+            Jar jar = new Jar(fileName(url.getPath()));
+            addClose(jar);
+            URLConnection connection = url.openConnection();
+            InputStream in = connection.getInputStream();
+            long lastModified = connection.getLastModified();
+            if (lastModified == 0)
+                // We assume the worst :-(
+                lastModified = System.currentTimeMillis();
+            EmbeddedResource.build(jar, in, lastModified);
+            in.close();
+            return jar;
+        } catch (IOException ee) {
+            // Check if we have files on the classpath
+            // that have the right name, allows us to specify those
+            // names instead of the full path.
+            for (Iterator<Jar> cp = getClasspath().iterator(); cp.hasNext();) {
+                Jar entry = cp.next();
+                if (entry.source != null && entry.source.getName().equals(name)) {
+                    return entry;
+                }
+            }
+            // error("Can not find jar file for " + from + ": " + name);
+        }
+        return null;
+    }
+
+    private String fileName(String path) {
+        int n = path.lastIndexOf('/');
+        if (n > 0)
+            return path.substring(n + 1);
+        return path;
+    }
+
+    /**
+     * 
+     * @param manifest
+     * @throws Exception
+     */
+    void merge(Manifest result, Manifest old) throws IOException {
+        if (old != null) {
+            for (Iterator<Map.Entry<Object, Object>> e = old
+                    .getMainAttributes().entrySet().iterator(); e.hasNext();) {
+                Map.Entry<Object, Object> entry = e.next();
+                Attributes.Name name = (Attributes.Name) entry.getKey();
+                String value = (String) entry.getValue();
+                if (name.toString().equalsIgnoreCase("Created-By"))
+                    name = new Attributes.Name("Originally-Created-By");
+                if (!result.getMainAttributes().containsKey(name))
+                    result.getMainAttributes().put(name, value);
+            }
+
+            // do not overwrite existing entries
+            Map<String, Attributes> oldEntries = old.getEntries();
+            Map<String, Attributes> newEntries = result.getEntries();
+            for (Iterator<Map.Entry<String, Attributes>> e = oldEntries
+                    .entrySet().iterator(); e.hasNext();) {
+                Map.Entry<String, Attributes> entry = e.next();
+                if (!newEntries.containsKey(entry.getKey())) {
+                    newEntries.put(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+    }
+
+    String stem(String name) {
+        int n = name.lastIndexOf('.');
+        if (n > 0)
+            return name.substring(0, n);
+        else
+            return name;
+    }
+
+    /**
+     * Bnd is case sensitive for the instructions so we better check people are
+     * not using an invalid case. We do allow this to set headers that should
+     * not be processed by us but should be used by the framework.
+     * 
+     * @param properties
+     *            Properties to verify.
+     */
+
+    void verifyManifestHeadersCase(Properties properties) {
+        for (Iterator<Object> i = properties.keySet().iterator(); i.hasNext();) {
+            String header = (String) i.next();
+            for (int j = 0; j < headers.length; j++) {
+                if (!headers[j].equals(header)
+                        && headers[j].equalsIgnoreCase(header)) {
+                    warning("Using a standard OSGi header with the wrong case (bnd is case sensitive!), using: "
+                            + header + " and expecting: " + headers[j]);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * We will add all exports to the imports unless there is a -noimport
+     * directive specified on an export. This directive is skipped for the
+     * manifest.
+     * 
+     * We also remove any version parameter so that augmentImports can do the
+     * version policy.
+     * 
+     */
+    Map<String, Map<String, String>> addExportsToImports(
+            Map<String, Map<String, String>> exports) {
+        Map<String, Map<String, String>> importsFromExports = newHashMap();
+        for (Map.Entry<String, Map<String, String>> packageEntry : exports
+                .entrySet()) {
+            String packageName = packageEntry.getKey();
+            Map<String, String> parameters = packageEntry.getValue();
+            String noimport = (String) parameters.get(NO_IMPORT_DIRECTIVE);
+            if (noimport == null || !noimport.equalsIgnoreCase("true")) {
+                if (parameters.containsKey(VERSION_ATTRIBUTE)) {
+                    parameters = newMap(parameters);
+                    parameters.remove(VERSION_ATTRIBUTE);
+                }
+                importsFromExports.put(packageName, parameters);
+            }
+        }
+        return importsFromExports;
+    }
+
+    /**
+     * Create the imports/exports by parsing
+     * 
+     * @throws IOException
+     */
+    void analyzeClasspath() throws IOException {
+        classpathExports = newHashMap();
+        for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
+            Jar current = c.next();
+            checkManifest(current);
+            for (Iterator<String> j = current.getDirectories().keySet()
+                    .iterator(); j.hasNext();) {
+                String dir = j.next();
+                Resource resource = current.getResource(dir + "/packageinfo");
+                if (resource != null) {
+                    InputStream in = resource.openInputStream();
+                    try {
+                        String version = parsePackageInfo(in);
+                        setPackageInfo(dir, "version", version);
+                    } finally {
+                        in.close();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 
+     * @param jar
+     */
+    void checkManifest(Jar jar) {
+        try {
+            Manifest m = jar.getManifest();
+            if (m != null) {
+                String exportHeader = m.getMainAttributes().getValue(
+                        EXPORT_PACKAGE);
+                if (exportHeader != null) {
+                    Map<String, Map<String, String>> exported = parseHeader(exportHeader);
+                    if (exported != null)
+                        classpathExports.putAll(exported);
+                }
+            }
+        } catch (Exception e) {
+            warning("Erroneous Manifest for " + jar + " " + e);
+        }
+    }
+
+    /**
+     * Find some more information about imports in manifest and other places.
+     */
+    void augmentImports() {
+        for (String packageName : imports.keySet()) {
+            setProperty(CURRENT_PACKAGE, packageName);
+            try {
+                Map<String, String> importAttributes = imports.get(packageName);
+                Map<String, String> exporterAttributes = classpathExports
+                        .get(packageName);
+                if (exporterAttributes == null)
+                    exporterAttributes = exports.get(packageName);
+
+                if (exporterAttributes != null) {
+                    augmentVersion(importAttributes, exporterAttributes);
+                    augmentMandatory(importAttributes, exporterAttributes);
+                    if (exporterAttributes.containsKey(IMPORT_DIRECTIVE))
+                        importAttributes.put(IMPORT_DIRECTIVE,
+                                exporterAttributes.get(IMPORT_DIRECTIVE));
+                }
+
+                // Convert any attribute values that have macros.
+                for (String key : importAttributes.keySet()) {
+                    String value = importAttributes.get(key);
+                    if (value.indexOf('$') >= 0) {
+                        value = getReplacer().process(value);
+                        importAttributes.put(key, value);
+                    }
+                }
+
+                // You can add a remove-attribute: directive with a regular
+                // expression for attributes that need to be removed. We also
+                // remove all attributes that have a value of !. This allows
+                // you to use macros with ${if} to remove values.
+                String remove = importAttributes
+                        .remove(REMOVE_ATTRIBUTE_DIRECTIVE);
+                Instruction removeInstr = null;
+
+                if (remove != null)
+                    removeInstr = Instruction.getPattern(remove);
+
+                for (Iterator<Map.Entry<String, String>> i = importAttributes
+                        .entrySet().iterator(); i.hasNext();) {
+                    Map.Entry<String, String> entry = i.next();
+                    if (entry.getValue().equals("!"))
+                        i.remove();
+                    else if (removeInstr != null
+                            && removeInstr.matches((String) entry.getKey()))
+                        i.remove();
+                    else {
+                        // Not removed ...
+                    }
+                }
+
+            } finally {
+                unsetProperty(CURRENT_PACKAGE);
+            }
+        }
+    }
+
+    /**
+     * If we use an import with mandatory attributes we better all use them
+     * 
+     * @param currentAttributes
+     * @param exporter
+     */
+    private void augmentMandatory(Map<String, String> currentAttributes,
+            Map<String, String> exporter) {
+        String mandatory = (String) exporter.get("mandatory:");
+        if (mandatory != null) {
+            String[] attrs = mandatory.split("\\s*,\\s*");
+            for (int i = 0; i < attrs.length; i++) {
+                if (!currentAttributes.containsKey(attrs[i]))
+                    currentAttributes.put(attrs[i], exporter.get(attrs[i]));
+            }
+        }
+    }
+
+    /**
+     * Check if we can augment the version from the exporter.
+     * 
+     * We allow the version in the import to specify a @ which is replaced with
+     * the exporter's version.
+     * 
+     * @param currentAttributes
+     * @param exporter
+     */
+    private void augmentVersion(Map<String, String> currentAttributes,
+            Map<String, String> exporter) {
+
+        String exportVersion = (String) exporter.get("version");
+        if (exportVersion == null)
+            exportVersion = (String) exporter.get("specification-version");
+        if (exportVersion == null)
+            return;
+
+        exportVersion = cleanupVersion(exportVersion);
+
+        setProperty("@", exportVersion);
+
+        String importRange = currentAttributes.get("version");
+        if (importRange != null) {
+            importRange = cleanupVersion(importRange);
+            importRange = getReplacer().process(importRange);
+        } else
+            importRange = getVersionPolicy();
+
+        unsetProperty("@");
+
+        // See if we can borrow the version
+        // we mist replace the ${@} with the version we
+        // found this can be useful if you want a range to start
+        // with the found version.
+        currentAttributes.put("version", importRange);
+    }
+
+    /**
+     * Add the uses clauses
+     * 
+     * @param exports
+     * @param uses
+     * @throws MojoExecutionException
+     */
+    void doUses(Map<String, Map<String, String>> exports,
+            Map<String, Set<String>> uses,
+            Map<String, Map<String, String>> imports) {
+        if ("true".equalsIgnoreCase(getProperty(NOUSES)))
+            return;
+
+        for (Iterator<String> i = exports.keySet().iterator(); i.hasNext();) {
+            String packageName = i.next();
+            setProperty(CURRENT_PACKAGE, packageName);
+            try {
+                Map<String, String> clause = exports.get(packageName);
+                String override = clause.get(USES_DIRECTIVE);
+                if (override == null)
+                    override = USES_USES;
+
+                Set<String> usedPackages = uses.get(packageName);
+                if (usedPackages != null) {
+                    // Only do a uses on exported or imported packages
+                    // and uses should also not contain our own package
+                    // name
+                    Set<String> sharedPackages = new HashSet<String>();
+                    sharedPackages.addAll(imports.keySet());
+                    sharedPackages.addAll(exports.keySet());
+                    usedPackages.retainAll(sharedPackages);
+                    usedPackages.remove(packageName);
+
+                    StringBuffer sb = new StringBuffer();
+                    String del = "";
+                    for (Iterator<String> u = usedPackages.iterator(); u
+                            .hasNext();) {
+                        String usedPackage = u.next();
+                        if (!usedPackage.startsWith("java.")) {
+                            sb.append(del);
+                            sb.append(usedPackage);
+                            del = ",";
+                        }
+                    }
+                    if (override.indexOf('$') >= 0) {
+                        setProperty(CURRENT_USES, sb.toString());
+                        override = getReplacer().process(override);
+                        unsetProperty(CURRENT_USES);
+                    } else
+                        // This is for backward compatibility 0.0.287
+                        // can be deprecated over time
+                        override = override
+                                .replaceAll(USES_USES, sb.toString()).trim();
+                    if (override.endsWith(","))
+                        override = override.substring(0, override.length() - 1);
+                    if (override.startsWith(","))
+                        override = override.substring(1);
+                    if (override.length() > 0) {
+                        clause.put("uses:", override);
+                    }
+                }
+            } finally {
+                unsetProperty(CURRENT_PACKAGE);
+            }
+        }
+    }
+
+    /**
+     * Transitively remove all elemens from unreachable through the uses link.
+     * 
+     * @param name
+     * @param unreachable
+     */
+    void removeTransitive(String name, Set<String> unreachable) {
+        if (!unreachable.contains(name))
+            return;
+
+        unreachable.remove(name);
+
+        Set<String> ref = uses.get(name);
+        if (ref != null) {
+            for (Iterator<String> r = ref.iterator(); r.hasNext();) {
+                String element = (String) r.next();
+                removeTransitive(element, unreachable);
+            }
+        }
+    }
+
+    /**
+     * Helper method to set the package info
+     * 
+     * @param dir
+     * @param key
+     * @param value
+     */
+    void setPackageInfo(String dir, String key, String value) {
+        if (value != null) {
+            String pack = dir.replace('/', '.');
+            Map<String, String> map = classpathExports.get(pack);
+            if (map == null) {
+                map = new HashMap<String, String>();
+                classpathExports.put(pack, map);
+            }
+            map.put(key, value);
+        }
+    }
+
+    public void close() {
+        super.close();
+        if (dot != null)
+            dot.close();
+
+        if (classpath != null)
+            for (Iterator<Jar> j = classpath.iterator(); j.hasNext();) {
+                Jar jar = j.next();
+                jar.close();
+            }
+    }
+
+    /**
+     * Findpath looks through the contents of the JAR and finds paths that end
+     * with the given regular expression
+     * 
+     * ${findpath (; reg-expr (; replacement)? )? }
+     * 
+     * @param args
+     * @return
+     */
+    public String _findpath(String args[]) {
+        return findPath("findpath", args, true);
+    }
+
+    public String _findname(String args[]) {
+        return findPath("findname", args, false);
+    }
+
+    String findPath(String name, String[] args, boolean fullPathName) {
+        if (args.length > 3) {
+            warning("Invalid nr of arguments to " + name + " "
+                    + Arrays.asList(args) + ", syntax: ${" + name
+                    + " (; reg-expr (; replacement)? )? }");
+            return null;
+        }
+
+        String regexp = ".*";
+        String replace = null;
+
+        switch (args.length) {
+        case 3:
+            replace = args[2];
+        case 2:
+            regexp = args[1];
+        }
+        StringBuffer sb = new StringBuffer();
+        String del = "";
+
+        Pattern expr = Pattern.compile(regexp);
+        for (Iterator<String> e = dot.getResources().keySet().iterator(); e
+                .hasNext();) {
+            String path = e.next();
+            if (!fullPathName) {
+                int n = path.lastIndexOf('/');
+                if (n >= 0) {
+                    path = path.substring(n + 1);
+                }
+            }
+
+            Matcher m = expr.matcher(path);
+            if (m.matches()) {
+                if (replace != null)
+                    path = m.replaceAll(replace);
+
+                sb.append(del);
+                sb.append(path);
+                del = ", ";
+            }
+        }
+        return sb.toString();
+    }
+
+    public void putAll(Map<String, String> additional, boolean force) {
+        for (Iterator<Map.Entry<String, String>> i = additional.entrySet()
+                .iterator(); i.hasNext();) {
+            Map.Entry<String, String> entry = i.next();
+            if (force || getProperties().get(entry.getKey()) == null)
+                setProperty((String) entry.getKey(), (String) entry.getValue());
+        }
+    }
+
+    boolean firstUse = true;
+
+    public List<Jar> getClasspath() {
+        if (firstUse) {
+            firstUse = false;
+            String cp = getProperty(CLASSPATH);
+            if (cp != null)
+                for (String s : split(cp)) {
+                    Jar jar = getJarFromName(s, "getting classpath");
+                    if (jar != null)
+                        addClasspath(jar);
+                }
+        }
+        return classpath;
+    }
+
+    public void addClasspath(Jar jar) {
+        if (isPedantic() && jar.getResources().isEmpty())
+            warning("There is an empty jar or directory on the classpath: "
+                    + jar.getName());
+
+        classpath.add(jar);
+    }
+
+    public void addClasspath(File cp) throws IOException {
+        if (!cp.exists())
+            warning("File on classpath that does not exist: " + cp);
+        Jar jar = new Jar(cp);
+        addClose(jar);
+        classpath.add(jar);
+    }
+
+    public void clear() {
+        classpath.clear();
+    }
+
+    public Jar getTarget() {
+        return dot;
+    }
+
+    public Map<String, Clazz> analyzeBundleClasspath(Jar dot,
+            Map<String, Map<String, String>> bundleClasspath,
+            Map<String, Map<String, String>> contained,
+            Map<String, Map<String, String>> referred,
+            Map<String, Set<String>> uses) throws IOException {
+        Map<String, Clazz> classSpace = new HashMap<String, Clazz>();
+
+        if (bundleClasspath.isEmpty()) {
+            analyzeJar(dot, "", classSpace, contained, referred, uses);
+        } else {
+            for (String path : bundleClasspath.keySet()) {
+                if (path.equals(".")) {
+                    analyzeJar(dot, "", classSpace, contained, referred, uses);
+                    continue;
+                }
+                //
+                // There are 3 cases:
+                // - embedded JAR file
+                // - directory
+                // - error
+                //
+
+                Resource resource = dot.getResource(path);
+                if (resource != null) {
+                    try {
+                        Jar jar = new Jar(path);
+                        addClose(jar);
+                        EmbeddedResource.build(jar, resource);
+                        analyzeJar(jar, "", classSpace, contained, referred,
+                                uses);
+                    } catch (Exception e) {
+                        warning("Invalid bundle classpath entry: " + path + " "
+                                + e);
+                    }
+                } else {
+                    if (dot.getDirectories().containsKey(path)) {
+                        analyzeJar(dot, path + '/', classSpace, contained,
+                                referred, uses);
+                    } else {
+                        warning("No sub JAR or directory " + path);
+                    }
+                }
+            }
+        }
+        return classSpace;
+    }
+
+    /**
+     * We traverse through all the classes that we can find and calculate the
+     * contained and referred set and uses. This method ignores the Bundle
+     * classpath.
+     * 
+     * @param jar
+     * @param contained
+     * @param referred
+     * @param uses
+     * @throws IOException
+     */
+    private void analyzeJar(Jar jar, String prefix,
+            Map<String, Clazz> classSpace,
+            Map<String, Map<String, String>> contained,
+            Map<String, Map<String, String>> referred,
+            Map<String, Set<String>> uses) throws IOException {
+
+        next: for (String path : jar.getResources().keySet()) {
+            if (path.startsWith(prefix)) {
+                String relativePath = path.substring(prefix.length());
+                String pack = getPackage(relativePath);
+
+                if (pack != null && !contained.containsKey(pack)) {
+                    if (!isMetaData(relativePath)) {
+
+                        Map<String, String> map = new LinkedHashMap<String, String>();
+                        contained.put(pack, map);
+                        Resource pinfo = jar.getResource(prefix
+                                + pack.replace('.', '/') + "/packageinfo");
+                        if (pinfo != null) {
+                            InputStream in = pinfo.openInputStream();
+                            String version = parsePackageInfo(in);
+                            in.close();
+                            if (version != null)
+                                map.put("version", version);
+                        }
+                    }
+                }
+
+                if (path.endsWith(".class")) {
+                    Resource resource = jar.getResource(path);
+                    Clazz clazz;
+
+                    try {
+                        InputStream in = resource.openInputStream();
+                        clazz = new Clazz(relativePath, resource);
+                        clazz.parseClassFile();
+                        in.close();
+                    } catch (Throwable e) {
+                        error("Invalid class file: " + relativePath, e);
+                        e.printStackTrace();
+                        continue next;
+                    }
+
+                    String calculatedPath = clazz.getClassName() + ".class";
+                    if (!calculatedPath.equals(relativePath))
+                        error("Class in different directory than declared. Path from class name is "
+                                + calculatedPath
+                                + " but the path in the jar is "
+                                + relativePath
+                                + " from " + jar);
+
+                    classSpace.put(relativePath, clazz);
+                    referred.putAll(clazz.getReferred());
+
+                    // Add all the used packages
+                    // to this package
+                    Set<String> t = uses.get(pack);
+                    if (t == null)
+                        uses.put(pack, t = new LinkedHashSet<String>());
+                    t.addAll(clazz.getReferred().keySet());
+                    t.remove(pack);
+                }
+            }
+        }
+    }
+
+    /**
+     * Clean up version parameters. Other builders use more fuzzy definitions of
+     * the version syntax. This method cleans up such a version to match an OSGi
+     * version.
+     * 
+     * @param VERSION_STRING
+     * @return
+     */
+    static Pattern fuzzyVersion      = Pattern
+                                             .compile(
+                                                     "(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+                                                     Pattern.DOTALL);
+    static Pattern fuzzyVersionRange = Pattern
+                                             .compile(
+                                                     "(\\(|\\[)\\s*([-\\da-zA-Z.]+)\\s*,\\s*([-\\da-zA-Z.]+)\\s*(\\]|\\))",
+                                                     Pattern.DOTALL);
+    static Pattern fuzzyModifier     = Pattern.compile("(\\d+[.-])*(.*)",
+                                             Pattern.DOTALL);
+
+    static Pattern nummeric          = Pattern.compile("\\d*");
+
+    static public String cleanupVersion(String version) {
+        if (Verifier.VERSIONRANGE.matcher(version).matches())
+            return version;
+
+        Matcher m = fuzzyVersionRange.matcher(version);
+        if (m.matches()) {
+            String prefix = m.group(1);
+            String first = m.group(2);
+            String last = m.group(3);
+            String suffix = m.group(4);
+            return prefix + cleanupVersion(first) + "," + cleanupVersion(last)
+                    + suffix;
+        } else {
+            m = fuzzyVersion.matcher(version);
+            if (m.matches()) {
+                StringBuffer result = new StringBuffer();
+                String major = m.group(1);
+                String minor = m.group(3);
+                String micro = m.group(5);
+                String qualifier = m.group(7);
+
+                if (major != null) {
+                    result.append(major);
+                    if (minor != null) {
+                        result.append(".");
+                        result.append(minor);
+                        if (micro != null) {
+                            result.append(".");
+                            result.append(micro);
+                            if (qualifier != null) {
+                                result.append(".");
+                                cleanupModifier(result, qualifier);
+                            }
+                        } else if (qualifier != null) {
+                            result.append(".0.");
+                            cleanupModifier(result, qualifier);
+                        }
+                    } else if (qualifier != null) {
+                        result.append(".0.0.");
+                        cleanupModifier(result, qualifier);
+                    }
+                    return result.toString();
+                }
+            }
+        }
+        return version;
+    }
+
+    static void cleanupModifier(StringBuffer result, String modifier) {
+        Matcher m = fuzzyModifier.matcher(modifier);
+        if (m.matches())
+            modifier = m.group(2);
+
+        for (int i = 0; i < modifier.length(); i++) {
+            char c = modifier.charAt(i);
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+                result.append(c);
+        }
+    }
+
+    /**
+     * Decide if the package is a metadata package.
+     * 
+     * @param pack
+     * @return
+     */
+    boolean isMetaData(String pack) {
+        for (int i = 0; i < METAPACKAGES.length; i++) {
+            if (pack.startsWith(METAPACKAGES[i]))
+                return true;
+        }
+        return false;
+    }
+
+    public String getPackage(String clazz) {
+        int n = clazz.lastIndexOf('/');
+        if (n < 0)
+            return ".";
+        return clazz.substring(0, n).replace('/', '.');
+    }
+
+    //
+    // We accept more than correct OSGi versions because in a later
+    // phase we actually cleanup maven versions. But it is a bit yucky
+    //
+    static String parsePackageInfo(InputStream jar) throws IOException {
+        try {
+            Properties p = new Properties();
+            p.load(jar);
+            jar.close();
+            if (p.containsKey("version")) {
+                return p.getProperty("version");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String getVersionPolicy() {
+        return getProperty(VERSIONPOLICY, "${version;==;${@}}");
+    }
+
+    /**
+     * The extends macro traverses all classes and returns a list of class names
+     * that extend a base class.
+     */
+
+    static String _classesHelp = "${classes;'implementing'|'extending'|'importing'|'named'|'version'|'any';<pattern>}, Return a list of class fully qualified class names that extend/implement/import any of the contained classes matching the pattern\n";
+
+    public String _classes(String... args) {
+        // Macro.verifyCommand(args, _classesHelp, new
+        // Pattern[]{null,Pattern.compile("(implementing|implements|extending|extends|importing|imports|any)"),
+        // null}, 3,3);
+
+        Collection<Clazz> matched = getClasses(args);
+        if (matched.isEmpty())
+            return "";
+
+        return join(matched);
+    }
+
+    public Collection<Clazz> getClasses(String... args) {
+
+        Set<Clazz> matched = new HashSet<Clazz>(classspace.values());
+        for (int i = 1; i < args.length; i++) {
+            if (args.length < i + 1)
+                throw new IllegalArgumentException(
+                        "${classes} macro must have odd number of arguments. "
+                                + _classesHelp);
+
+            String typeName = args[i];
+            if (typeName.equalsIgnoreCase("extending"))
+                typeName = "extends";
+            else if (typeName.equalsIgnoreCase("importing"))
+                typeName = "imports";
+            else if (typeName.equalsIgnoreCase("implementing"))
+                typeName = "implements";
+
+            Clazz.QUERY type = Clazz.QUERY.valueOf(typeName.toUpperCase());
+
+            if (type == null)
+                throw new IllegalArgumentException(
+                        "${classes} has invalid type: " + typeName + ". "
+                                + _classesHelp);
+
+            Instruction instr = null;
+            if (Clazz.HAS_ARGUMENT.contains(type)) {
+                // The argument is declared as a dotted name but the classes
+                // use a slashed named. So convert the name before we make it a
+                // instruction.
+                String pattern = args[++i].replace('.', '/');
+                instr = Instruction.getPattern(pattern);
+            }
+            for (Iterator<Clazz> c = matched.iterator(); c.hasNext();) {
+                Clazz clazz = c.next();
+                if (!clazz.is(type, instr, classspace)) {
+                    c.remove();
+                }
+            }
+        }
+        return matched;
+    }
+
+    /**
+     * Get the exporter of a package ...
+     */
+
+    public String _exporters(String args[]) throws Exception {
+        Macro
+                .verifyCommand(
+                        args,
+                        "${exporters;<packagename>}, returns the list of jars that export the given package",
+                        null, 2, 2);
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+        String pack = args[1].replace('.', '/');
+        for (Jar jar : classpath) {
+            if (jar.getDirectories().containsKey(pack)) {
+                sb.append(del);
+                sb.append(jar.getName());
+            }
+        }
+        return sb.toString();
+    }
+
+    public Map<String, Clazz> getClassspace() {
+        return classspace;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Annotation.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Annotation.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Annotation.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,35 @@
+package aQute.lib.osgi;
+
+import java.lang.annotation.*;
+import java.util.*;
+
+public class Annotation {
+    String              name;
+    Map<String, Object> elements;
+    ElementType member;
+    RetentionPolicy policy;
+    
+    public Annotation(String name, Map<String, Object> elements, ElementType member, RetentionPolicy policy ) {
+        this.name = name;
+        this.elements = elements;
+        this.member = member;
+        this.policy = policy;
+    }
+    
+    
+    public String getName() {
+        return name;
+    }
+    public String toString() {
+        return name  + ":" + member + ":" + policy +":" +elements;
+    }
+
+
+    public <T> T get(String string) {
+        if ( elements == null )
+            return null;
+        
+        return (T) elements.get(string);
+    }
+    
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Builder.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Builder.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Builder.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1134 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import aQute.bnd.make.*;
+import aQute.bnd.service.*;
+
+/**
+ * Include-Resource: ( [name '=' ] file )+
+ * 
+ * Private-Package: package-decl ( ',' package-decl )*
+ * 
+ * Export-Package: package-decl ( ',' package-decl )*
+ * 
+ * Import-Package: package-decl ( ',' package-decl )*
+ * 
+ * @version $Revision: 1.20 $
+ */
+public class Builder extends Analyzer {
+    private static final int    SPLIT_MERGE_LAST  = 1;
+    private static final int    SPLIT_MERGE_FIRST = 2;
+    private static final int    SPLIT_ERROR       = 3;
+    private static final int    SPLIT_FIRST       = 4;
+    private static final int    SPLIT_DEFAULT     = 0;
+
+    private static final File[] EMPTY_FILE        = new File[0];
+
+    List<File>                  sourcePath        = new ArrayList<File>();
+    Pattern                     NAME_URL          = Pattern
+                                                          .compile("(.*)(http://.*)");
+
+    Make                        make              = new Make(this);
+
+    public Builder(Processor parent) {
+        super(parent);
+    }
+
+    public Builder() {
+    }
+
+    public Jar build() throws Exception {
+        if (getProperty(NOPE) != null)
+            return null;
+
+        String sub = getProperty(SUB);
+        if (sub != null && sub.trim().length() > 0)
+            error("Specified "
+                    + SUB
+                    + " but calls build() instead of builds() (might be a programmer error)");
+
+        if (getProperty(CONDUIT) != null)
+            error("Specified "
+                    + CONDUIT
+                    + " but calls build() instead of builds() (might be a programmer error");
+
+        dot = new Jar("dot");
+        addClose(dot);
+        try {
+            long modified = Long.parseLong(getProperty("base.modified"));
+            dot.updateModified(modified, "Base modified");
+        } catch (Exception e) {
+        }
+
+        doExpand(dot);
+        doIncludeResources(dot);
+
+        doConditional(dot);
+
+        // NEW!
+        // Check if we override the calculation of the
+        // manifest. We still need to calculated it because
+        // we need to have analyzed the classpath.
+
+        Manifest manifest = calcManifest();
+
+        String mf = getProperty(MANIFEST);
+        if (mf != null) {
+            File mff = getFile(mf);
+            if (mff.isFile()) {
+                try {
+                    InputStream in = new FileInputStream(mff);
+                    manifest = new Manifest(in);
+                    in.close();
+                } catch (Exception e) {
+                    error(MANIFEST + " while reading manifest file", e);
+                }
+            } else {
+                error(MANIFEST + ", no such file " + mf);
+            }
+        }
+
+        if (getProperty(NOMANIFEST) == null)
+            dot.setManifest(manifest);
+        else
+            dot.setNoManifest(true);
+
+        // This must happen after we analyzed so
+        // we know what it is on the classpath
+        addSources(dot);
+        if (getProperty(POM) != null)
+            doPom(dot);
+
+        doVerify(dot);
+
+        if (dot.getResources().isEmpty())
+            error("The JAR is empty");
+
+        dot.updateModified(lastModified(), "Last Modified Processor");
+        dot.setName(getBsn());
+
+        sign(dot);
+        return dot;
+    }
+
+    /**
+     * Sign the jar file.
+     * 
+     * -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
+     * <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
+     * 
+     * @return
+     */
+
+    void sign(Jar jar) throws Exception {
+        String signing = getProperty("-sign");
+        if (signing == null)
+            return;
+
+        trace("Signing %s, with %s", getBsn(), signing);
+        List<SignerPlugin> signers = getPlugins(SignerPlugin.class);
+
+        Map<String, Map<String, String>> infos = parseHeader(signing);
+        for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
+            for (SignerPlugin signer : signers) {
+                signer.sign(this, entry.getKey());
+            }
+        }
+    }
+
+    public boolean hasSources() {
+        return isTrue(getProperty(SOURCES));
+    }
+
+    protected String getImportPackages() {
+        String ip = super.getImportPackages();
+        if (ip != null)
+            return ip;
+
+        return "*";
+    }
+
+    private void doConditional(Jar dot) throws IOException {
+        Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
+        if ( conditionals.isEmpty() )
+            return;
+        
+        int size;
+        do {
+            size = dot.getDirectories().size();
+            analyze();
+            analyzed = false;
+            Map<String, Map<String, String>> imports = getImports();
+
+            // Match the packages specified in conditionals
+            // against the imports. Any match must become a
+            // Private-Package
+            Map<String, Map<String, String>> filtered = merge(
+                    CONDITIONAL_PACKAGE, conditionals, imports,
+                    new HashSet<String>(), null);
+
+            // Imports can also specify a private import. These
+            // packages must also be copied to the bundle
+            for (Map.Entry<String, Map<String, String>> entry : getImports()
+                    .entrySet()) {
+                String type = entry.getValue().get(IMPORT_DIRECTIVE);
+                if (type != null && type.equals("private"))
+                    filtered.put(entry.getKey(), entry.getValue());
+            }
+
+            // remove existing packages to prevent merge errors
+            filtered.keySet().removeAll(dot.getPackages());
+            doExpand(dot, CONDITIONAL_PACKAGE + " Private imports",
+                    replaceWitInstruction(filtered, CONDITIONAL_PACKAGE), false);
+        } while (dot.getDirectories().size() > size);
+        analyzed = true;
+    }
+
+    /**
+     * Intercept the call to analyze and cleanup versions after we have analyzed
+     * the setup. We do not want to cleanup if we are going to verify.
+     */
+
+    public void analyze() throws IOException {
+        super.analyze();
+        cleanupVersion(imports);
+        cleanupVersion(exports);
+        String version = getProperty(BUNDLE_VERSION);
+        if (version != null)
+            setProperty(BUNDLE_VERSION, cleanupVersion(version));
+    }
+
+    public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
+        for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap
+                .entrySet().iterator(); e.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = e.next();
+            Map<String, String> attributes = entry.getValue();
+            if (attributes.containsKey("version")) {
+                attributes.put("version", cleanupVersion(attributes
+                        .get("version")));
+            }
+        }
+    }
+
+    /**
+     * 
+     */
+    private void addSources(Jar dot) {
+        if (!hasSources())
+            return;
+
+        Set<String> packages = new HashSet<String>();
+
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            getProperties().store(out, "Generated by BND, at " + new Date());
+            dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
+                    .toByteArray(), 0));
+            out.close();
+        } catch (Exception e) {
+            error("Can not embed bnd file in JAR: " + e);
+        }
+
+        for (Iterator<String> cpe = classspace.keySet().iterator(); cpe
+                .hasNext();) {
+            String path = cpe.next();
+            path = path.substring(0, path.length() - ".class".length())
+                    + ".java";
+            String pack = getPackage(path).replace('.', '/');
+            if (pack.length() > 1)
+                pack = pack + "/";
+            boolean found = false;
+            String[] fixed = { "packageinfo", "package.html",
+                    "module-info.java", "package-info.java" };
+            for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
+                File root = i.next();
+                File f = getFile(root, path);
+                if (f.exists()) {
+                    found = true;
+                    if (!packages.contains(pack)) {
+                        packages.add(pack);
+                        File bdir = getFile(root, pack);
+                        for (int j = 0; j < fixed.length; j++) {
+                            File ff = getFile(bdir, fixed[j]);
+                            if (ff.isFile()) {
+                                dot.putResource("OSGI-OPT/src/" + pack
+                                        + fixed[j], new FileResource(ff));
+                            }
+                        }
+                    }
+                    dot
+                            .putResource("OSGI-OPT/src/" + path,
+                                    new FileResource(f));
+                }
+            }
+            if (!found) {
+                for (Jar jar : classpath) {
+                    Resource resource = jar.getResource(path);
+                    if (resource != null) {
+                        dot.putResource("OSGI-OPT/src", resource);
+                    } else {
+                        resource = jar.getResource("OSGI-OPT/src/" + path);
+                        if (resource != null) {
+                            dot.putResource("OSGI-OPT/src", resource);
+                        }
+                    }
+                }
+            }
+            if (getSourcePath().isEmpty())
+                warning("Including sources but " + SOURCEPATH
+                        + " does not contain any source directories ");
+            // TODO copy from the jars where they came from
+        }
+    }
+
+    boolean firstUse = true;
+
+    public Collection<File> getSourcePath() {
+        if (firstUse) {
+            firstUse = false;
+            String sp = getProperty(SOURCEPATH);
+            if (sp != null) {
+                Map<String, Map<String, String>> map = parseHeader(sp);
+                for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
+                    String file = i.next();
+                    if (!isDuplicate(file)) {
+                        File f = getFile(file);
+                        if (!f.isDirectory()) {
+                            error("Adding a sourcepath that is not a directory: "
+                                    + f);
+                        } else {
+                            sourcePath.add(f);
+                        }
+                    }
+                }
+            }
+        }
+        return sourcePath;
+    }
+
+    private void doVerify(Jar dot) throws Exception {
+        Verifier verifier = new Verifier(dot, getProperties());
+        verifier.setPedantic(isPedantic());
+
+        // Give the verifier the benefit of our analysis
+        // prevents parsing the files twice
+        verifier.setClassSpace(classspace, contained, referred, uses);
+        verifier.verify();
+        getInfo(verifier);
+    }
+
+    private void doExpand(Jar jar) throws IOException {
+        if (getClasspath().size() == 0
+                && (getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
+            warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
+
+        Map<Instruction, Map<String, String>> privateMap = replaceWitInstruction(
+                getHeader(PRIVATE_PACKAGE), PRIVATE_PACKAGE);
+        Map<Instruction, Map<String, String>> exportMap = replaceWitInstruction(
+                getHeader(EXPORT_PACKAGE), EXPORT_PACKAGE);
+
+        if (isTrue(getProperty(Constants.UNDERTEST))) {
+            privateMap.putAll(replaceWitInstruction(parseHeader(getProperty(
+                    Constants.TESTPACKAGES, "test;presence:=optional")),
+                    TESTPACKAGES));
+        }
+        if (!privateMap.isEmpty())
+            doExpand(jar, "Private-Package, or -testpackages", privateMap, true);
+
+        if (!exportMap.isEmpty()) {
+            Jar exports = new Jar("exports");
+            doExpand(exports, "Export-Package", exportMap, true);
+            jar.addAll(exports);
+            exports.close();
+        }
+
+        if (privateMap.isEmpty() && exportMap.isEmpty() && !isResourceOnly()) {
+            warning("Neither Export-Package, Private-Package, -testpackages is set, therefore no packages will be included");
+        }
+    }
+
+    /**
+     * 
+     * @param jar
+     * @param name
+     * @param instructions
+     */
+    private void doExpand(Jar jar, String name,
+            Map<Instruction, Map<String, String>> instructions,
+            boolean mandatory) {
+        Set<Instruction> superfluous = removeMarkedDuplicates(instructions
+                .keySet());
+
+        for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
+            Jar now = c.next();
+            doExpand(jar, instructions, now, superfluous);
+        }
+
+        if (mandatory && superfluous.size() > 0) {
+            StringBuffer sb = new StringBuffer();
+            String del = "Instructions in " + name + " that are never used: ";
+            for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
+                Instruction p = i.next();
+                sb.append(del);
+                sb.append(p.getPattern());
+                del = ", ";
+            }
+            warning(sb.toString());
+        }
+    }
+
+    /**
+     * Iterate over each directory in the class path entry and check if that
+     * directory is a desired package.
+     * 
+     * @param included
+     * @param classpathEntry
+     */
+    private void doExpand(Jar jar,
+            Map<Instruction, Map<String, String>> included, Jar classpathEntry,
+            Set<Instruction> superfluous) {
+
+        loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
+                .getDirectories().entrySet()) {
+            String path = directory.getKey();
+
+            if (doNotCopy.matcher(getName(path)).matches())
+                continue;
+
+            if (directory.getValue() == null)
+                continue;
+
+            String pack = path.replace('/', '.');
+            Instruction instr = matches(included, pack, superfluous);
+            if (instr != null) {
+                // System.out.println("Pattern match: " + pack + " " +
+                // instr.getPattern() + " " + instr.isNegated());
+                if (!instr.isNegated()) {
+                    Map<String, Resource> contents = directory.getValue();
+
+                    // What to do with split packages? Well if this
+                    // directory already exists, we will check the strategy
+                    // and react accordingly.
+                    boolean overwriteResource = true;
+                    if (jar.hasDirectory(path)) {
+                        Map<String, String> directives = included.get(instr);
+
+                        switch (getSplitStrategy((String) directives
+                                .get(SPLIT_PACKAGE_DIRECTIVE))) {
+                        case SPLIT_MERGE_LAST:
+                            overwriteResource = true;
+                            break;
+
+                        case SPLIT_MERGE_FIRST:
+                            overwriteResource = false;
+                            break;
+
+                        case SPLIT_ERROR:
+                            error(diagnostic(pack, getClasspath(),
+                                    classpathEntry.source));
+                            continue loop;
+
+                        case SPLIT_FIRST:
+                            continue loop;
+
+                        default:
+                            warning(diagnostic(pack, getClasspath(),
+                                    classpathEntry.source));
+                            overwriteResource = false;
+                            break;
+                        }
+                    }
+
+                    jar.addDirectory(contents, overwriteResource);
+
+                    String key = path + "/bnd.info";
+                    Resource r = jar.getResource(key);
+                    if (r != null)
+                        jar.putResource(key, new PreprocessResource(this, r));
+
+                    if (hasSources()) {
+                        String srcPath = "OSGI-OPT/src/" + path;
+                        Map<String, Resource> srcContents = classpathEntry
+                                .getDirectories().get(srcPath);
+                        if (srcContents != null) {
+                            jar.addDirectory(srcContents, overwriteResource);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Analyze the classpath for a split package
+     * 
+     * @param pack
+     * @param classpath
+     * @param source
+     * @return
+     */
+    private String diagnostic(String pack, List<Jar> classpath, File source) {
+        // Default is like merge-first, but with a warning
+        // Find the culprits
+        pack = pack.replace('.', '/');
+        List<Jar> culprits = new ArrayList<Jar>();
+        for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
+            Jar culprit = (Jar) i.next();
+            if (culprit.getDirectories().containsKey(pack)) {
+                culprits.add(culprit);
+            }
+        }
+        return "Split package "
+                + pack
+                + "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
+                + "Package found in   " + culprits + "\n"
+                + "Reference from     " + source + "\n" + "Classpath          "
+                + classpath;
+    }
+
+    private int getSplitStrategy(String type) {
+        if (type == null)
+            return SPLIT_DEFAULT;
+
+        if (type.equals("merge-last"))
+            return SPLIT_MERGE_LAST;
+
+        if (type.equals("merge-first"))
+            return SPLIT_MERGE_FIRST;
+
+        if (type.equals("error"))
+            return SPLIT_ERROR;
+
+        if (type.equals("first"))
+            return SPLIT_FIRST;
+
+        error("Invalid strategy for split-package: " + type);
+        return SPLIT_DEFAULT;
+    }
+
+    private Instruction matches(
+            Map<Instruction, Map<String, String>> instructions, String pack,
+            Set<Instruction> superfluousPatterns) {
+        for (Instruction pattern : instructions.keySet()) {
+            if (pattern.matches(pack)) {
+                superfluousPatterns.remove(pattern);
+                return pattern;
+            }
+        }
+        return null;
+    }
+
+    private Map<String, Map<String, String>> getHeader(String string) {
+        if (string == null)
+            return Collections.emptyMap();
+        return parseHeader(getProperty(string));
+    }
+
+    /**
+     * Parse the Bundle-Includes header. Files in the bundles Include header are
+     * included in the jar. The source can be a directory or a file.
+     * 
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    private void doIncludeResources(Jar jar) throws Exception {
+        String includes = getProperty("Bundle-Includes");
+        if (includes == null) {
+            includes = getProperty(INCLUDERESOURCE);
+            if (includes == null)
+                includes = getProperty("Include-Resource");
+        } else
+            warning("Please use -includeresource instead of Bundle-Includes");
+
+        if (includes == null)
+            return;
+
+        Map<String, Map<String, String>> clauses = parseHeader(includes);
+
+        for (Iterator<Map.Entry<String, Map<String, String>>> i = clauses
+                .entrySet().iterator(); i.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = i.next();
+            doIncludeResource(jar, entry.getKey(), entry.getValue());
+        }
+    }
+
+    private void doIncludeResource(Jar jar, String name,
+            Map<String, String> extra) throws ZipException, IOException,
+            Exception {
+        boolean preprocess = false;
+        if (name.startsWith("{") && name.endsWith("}")) {
+            preprocess = true;
+            name = name.substring(1, name.length() - 1).trim();
+        }
+
+        if (name.startsWith("@")) {
+            extractFromJar(jar, name.substring(1));
+        } else if (extra.containsKey("literal")) {
+            String literal = (String) extra.get("literal");
+            Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
+            String x = (String) extra.get("extra");
+            if (x != null)
+                r.setExtra(x);
+            jar.putResource(name, r);
+        } else {
+            String source;
+            File sourceFile;
+            String destinationPath;
+
+            String parts[] = name.split("\\s*=\\s*");
+            if (parts.length == 1) {
+                // Just a copy, destination path defined by
+                // source path.
+                source = parts[0];
+                sourceFile = getFile(source);
+                // Directories should be copied to the root
+                // but files to their file name ...
+                if (sourceFile.isDirectory())
+                    destinationPath = "";
+                else
+                    destinationPath = sourceFile.getName();
+            } else {
+                source = parts[1];
+                sourceFile = getFile(source);
+                destinationPath = parts[0];
+
+                // Handle directories
+                if (sourceFile.isDirectory()) {
+                    destinationPath = doResourceDirectory(jar, extra,
+                            preprocess, sourceFile, destinationPath);
+                    return;
+                }
+            }
+
+            //destinationPath = checkDestinationPath(destinationPath);
+
+            if (!sourceFile.exists()) {
+                noSuchFile(jar, name, extra, source, destinationPath);
+            } else
+                copy(jar, destinationPath, sourceFile, preprocess, extra);
+        }
+    }
+
+    private String doResourceDirectory(Jar jar, Map<String, String> extra,
+            boolean preprocess, File sourceFile, String destinationPath)
+            throws Exception {
+        String filter = extra.get("filter:");
+        boolean flatten = isTrue(extra.get("flatten:"));
+        boolean recursive = true;
+        String directive = extra.get("recursive:");
+        if (directive != null) {
+            recursive = isTrue(directive);
+        }
+
+        InstructionFilter iFilter = null;
+        if (filter != null) {
+            iFilter = new InstructionFilter(Instruction
+                    .getPattern(filter), recursive);
+        } else {
+            iFilter = new InstructionFilter(null, recursive);
+        }
+
+        destinationPath = checkDestinationPath(destinationPath);
+
+        File[] files = resolveFiles(sourceFile, iFilter, recursive);
+        for (File file : files) {
+            String dp;
+            if (flatten) {
+                if (destinationPath.length() == 0) {
+                    dp = file.getName();
+                } else {
+                    dp = destinationPath + "/"
+                            + file.getName();
+                }
+            } else {
+                dp = destinationPath
+                        + file.getParentFile().getAbsolutePath()
+                                .substring(
+                                        sourceFile
+                                                .getAbsolutePath()
+                                                .length());
+                if (dp.length() > 0) {
+                    dp += "/" + file.getName();
+                } else {
+                    dp = file.getName();
+                }
+            }
+            copy(jar, dp, file, preprocess, extra);
+        }
+        return destinationPath;
+    }
+
+    private String checkDestinationPath(String destinationPath) {
+
+        // Some people insist on ending a directory with
+        // a slash ... it now also works if you do /=dir
+        if (destinationPath.endsWith("/"))
+            destinationPath = destinationPath.substring(0, destinationPath
+                    .length() - 1);
+        return destinationPath;
+    }
+
+    private File[] resolveFiles(File dir, FileFilter filter, boolean recursive) {
+        return resolveFiles(dir, filter, null, recursive);
+    }
+
+    private File[] resolveFiles(File dir, FileFilter filter, File[] files,
+            boolean recursive) {
+        if (files == null) {
+            files = EMPTY_FILE;
+        }
+
+        if (Analyzer.doNotCopy.matcher(dir.getName()).matches()) {
+            return files;
+        }
+
+        File[] fs = dir.listFiles(filter);
+        for (File file : fs) {
+            if (file.isDirectory()) {
+                if (recursive) {
+                    files = resolveFiles(file, filter, files, recursive);
+                }
+            } else {
+                if (files.length == 0) {
+                    files = new File[] { file };
+                } else {
+                    File[] newFiles = new File[files.length + 1];
+                    System.arraycopy(files, 0, newFiles, 0, files.length);
+                    newFiles[newFiles.length - 1] = file;
+                    files = newFiles;
+                }
+            }
+        }
+        return files;
+    }
+
+    private void noSuchFile(Jar jar, String clause, Map<String, String> extra,
+            String source, String destinationPath) throws Exception {
+        Jar src = getJarFromName(source, "Include-Resource " + source);
+        if (src != null) {
+            JarResource jarResource = new JarResource(src);
+            jar.putResource(destinationPath, jarResource);
+        } else {
+            Resource lastChance = make.process(source);
+            if (lastChance != null) {
+                String x = extra.get("extra");
+                if (x != null)
+                    lastChance.setExtra(x);
+                jar.putResource(destinationPath, lastChance);
+            } else
+                error("Input file does not exist: " + source);
+        }
+    }
+
+    /**
+     * Extra resources from a Jar and add them to the given jar. The clause is
+     * the
+     * 
+     * @param jar
+     * @param clauses
+     * @param i
+     * @throws ZipException
+     * @throws IOException
+     */
+    private void extractFromJar(Jar jar, String name) throws ZipException,
+            IOException {
+        // Inline all resources and classes from another jar
+        // optionally appended with a modified regular expression
+        // like @zip.jar!/META-INF/MANIFEST.MF
+        int n = name.lastIndexOf("!/");
+        Pattern filter = null;
+        if (n > 0) {
+            String fstring = name.substring(n + 2);
+            name = name.substring(0, n);
+            filter = wildcard(fstring);
+        }
+        Jar sub = getJarFromName(name, "extract from jar");
+        if (sub == null)
+            error("Can not find JAR file " + name);
+        else
+            jar.addAll(sub, filter);
+    }
+
+    private Pattern wildcard(String spec) {
+        StringBuffer sb = new StringBuffer();
+        for (int j = 0; j < spec.length(); j++) {
+            char c = spec.charAt(j);
+            switch (c) {
+            case '.':
+                sb.append("\\.");
+                break;
+
+            case '*':
+                // test for ** (all directories)
+                if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
+                    sb.append(".*");
+                    j++;
+                } else
+                    sb.append("[^/]*");
+                break;
+            default:
+                sb.append(c);
+                break;
+            }
+        }
+        String s = sb.toString();
+        try {
+            return Pattern.compile(s);
+        } catch (Exception e) {
+            error("Invalid regular expression on wildcarding: " + spec
+                    + " used *");
+        }
+        return null;
+    }
+
+    private void copy(Jar jar, String path, File from, boolean preprocess,
+            Map<String, String> extra) throws Exception {
+        if (doNotCopy.matcher(from.getName()).matches())
+            return;
+
+        if (from.isDirectory()) {
+            String next = path;
+            if (next.length() != 0 && ! next.endsWith("/"))
+                next += '/';
+
+            File files[] = from.listFiles();
+            for (int i = 0; i < files.length; i++) {
+                copy(jar, next + files[i].getName(), files[i], preprocess,
+                        extra);
+            }
+        } else {
+            if (from.exists()) {
+                Resource resource = new FileResource(from);
+                if (preprocess) {
+                    resource = new PreprocessResource(this, resource);
+                }
+                String x = extra.get("extra");
+                if (x != null)
+                    resource.setExtra(x);
+                if ( path.endsWith("/"))
+                    path = path + from.getName();
+                jar.putResource(path, resource);
+            } else {
+                error("Input file does not exist: " + from);
+            }
+        }
+    }
+
+    private String getName(String where) {
+        int n = where.lastIndexOf('/');
+        if (n < 0)
+            return where;
+
+        return where.substring(n + 1);
+    }
+
+    public void setSourcepath(File[] files) {
+        for (int i = 0; i < files.length; i++)
+            addSourcepath(files[i]);
+    }
+
+    public void addSourcepath(File cp) {
+        if (!cp.exists())
+            warning("File on sourcepath that does not exist: " + cp);
+
+        sourcePath.add(cp);
+    }
+
+    /**
+     * Create a POM reseource for Maven containing as much information as
+     * possible from the manifest.
+     * 
+     * @param output
+     * @param builder
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    public void doPom(Jar dot) throws FileNotFoundException, IOException {
+        {
+            Manifest manifest = dot.getManifest();
+            String name = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_NAME);
+            String description = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_DESCRIPTION);
+            String docUrl = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_DOCURL);
+            String version = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_VERSION);
+            String bundleVendor = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_VENDOR);
+            ByteArrayOutputStream s = new ByteArrayOutputStream();
+            PrintStream ps = new PrintStream(s);
+            String bsn = manifest.getMainAttributes().getValue(
+                    Analyzer.BUNDLE_SYMBOLICNAME);
+            String licenses = manifest.getMainAttributes().getValue(
+                    BUNDLE_LICENSE);
+
+            if (bsn == null) {
+                error("Can not create POM unless Bundle-SymbolicName is set");
+                return;
+            }
+
+            bsn = bsn.trim();
+            int n = bsn.lastIndexOf('.');
+            if (n <= 0) {
+                error("Can not create POM unless Bundle-SymbolicName contains a .");
+                ps.close();
+                s.close();
+                return;
+            }
+            String groupId = bsn.substring(0, n);
+            String artifactId = bsn.substring(n + 1);
+            ps
+                    .println("<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>");
+            ps.println("  <modelVersion>4.0.0</modelVersion>");
+            ps.println("  <groupId>" + groupId + "</groupId>");
+
+            n = artifactId.indexOf(';');
+            if (n > 0)
+                artifactId = artifactId.substring(0, n).trim();
+
+            ps.println("  <artifactId>" + artifactId + "</artifactId>");
+            ps.println("  <version>" + version + "</version>");
+            if (description != null) {
+                ps.println("  <description>");
+                ps.print("    ");
+                ps.println(description);
+                ps.println("  </description>");
+            }
+            if (name != null) {
+                ps.print("  <name>");
+                ps.print(name);
+                ps.println("</name>");
+            }
+            if (docUrl != null) {
+                ps.print("  <url>");
+                ps.print(docUrl);
+                ps.println("</url>");
+            }
+
+            if (bundleVendor != null) {
+                Matcher m = NAME_URL.matcher(bundleVendor);
+                String namePart = bundleVendor;
+                String urlPart = null;
+                if (m.matches()) {
+                    namePart = m.group(1);
+                    urlPart = m.group(2);
+                }
+                ps.println("  <organization>");
+                ps.print("    <name>");
+                ps.print(namePart.trim());
+                ps.println("</name>");
+                if (urlPart != null) {
+                    ps.print("    <url>");
+                    ps.print(urlPart.trim());
+                    ps.println("</url>");
+                }
+                ps.println("  </organization>");
+            }
+            if (licenses != null) {
+                ps.println("  <licenses>");
+                Map<String, Map<String, String>> map = parseHeader(licenses);
+                for (Iterator<Map.Entry<String, Map<String, String>>> e = map
+                        .entrySet().iterator(); e.hasNext();) {
+                    Map.Entry<String, Map<String, String>> entry = e.next();
+                    ps.println("    <license>");
+                    Map<String, String> values = entry.getValue();
+                    print(ps, values, "name", "name", (String) values
+                            .get("url"));
+                    print(ps, values, "url", "url", null);
+                    print(ps, values, "distribution", "distribution", "repo");
+                    ps.println("    </license>");
+                }
+                ps.println("  </licenses>");
+            }
+            ps.println("</project>");
+            ps.close();
+            s.close();
+            dot
+                    .putResource("pom.xml", new EmbeddedResource(s
+                            .toByteArray(), 0));
+        }
+    }
+
+    /**
+     * Utility function to print a tag from a map
+     * 
+     * @param ps
+     * @param values
+     * @param string
+     * @param tag
+     * @param object
+     */
+    private void print(PrintStream ps, Map<String, String> values,
+            String string, String tag, String object) {
+        String value = (String) values.get(string);
+        if (value == null)
+            value = object;
+        if (value == null)
+            return;
+        ps.println("    <" + tag + ">" + value.trim() + "</" + tag + ">");
+    }
+
+    public void close() {
+        super.close();
+    }
+
+    /**
+     * Build Multiple jars. If the -sub command is set, we filter the file with
+     * the given patterns.
+     * 
+     * @return
+     * @throws Exception
+     */
+    public Jar[] builds() throws Exception {
+        begin();
+
+        // Are we acting as a conduit for another JAR?
+        String conduit = getProperty(CONDUIT);
+        if (conduit != null) {
+            Map<String, Map<String, String>> map = parseHeader(conduit);
+            Jar[] result = new Jar[map.size()];
+            int n = 0;
+            for (String file : map.keySet()) {
+                Jar c = new Jar(getFile(file));
+                addClose(c);
+                String name = map.get(file).get("name");
+                if (name != null)
+                    c.setName(name);
+
+                result[n++] = c;
+            }
+            return result;
+        }
+
+        // If no -sub property, then reuse this builder object
+        // other wise, build all the sub parts.
+        String sub = getProperty(SUB);
+        if (sub == null) {
+            Jar jar = build();
+            if (jar == null)
+                return new Jar[0];
+
+            return new Jar[] { jar };
+        }
+
+        List<Jar> result = new ArrayList<Jar>();
+
+        // Get the Instruction objects that match the sub header
+        Set<Instruction> subs = replaceWitInstruction(parseHeader(sub), SUB)
+                .keySet();
+
+        // Get the member files of this directory
+        List<File> members = new ArrayList<File>(Arrays.asList(getBase()
+                .listFiles()));
+
+        getProperties().remove(SUB);
+        // For each member file
+        nextFile: while (members.size() > 0) {
+
+            File file = members.remove(0);
+            if (file.equals(getPropertiesFile()))
+                continue nextFile;
+
+            for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
+
+                Instruction instruction = i.next();
+                if (instruction.matches(file.getName())) {
+
+                    if (!instruction.isNegated()) {
+
+                        Builder builder = null;
+                        try {
+                            builder = getSubBuilder();
+                            addClose(builder);
+                            builder.setProperties(file);
+                            builder.setProperty(SUB, "");
+                            // Recursively build
+                            // TODO
+                            Jar jar = builder.build();
+                            jar.setName(builder.getBsn());
+                            result.add(jar);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            error("Sub Building " + file, e);
+                        }
+                        if (builder != null)
+                            getInfo(builder, file.getName() + ": ");
+                    }
+
+                    // Because we matched (even though we could be negated)
+                    // we skip any remaining searches
+                    continue nextFile;
+                }
+            }
+        }
+        setProperty(SUB, sub);
+        return result.toArray(new Jar[result.size()]);
+    }
+
+    public Builder getSubBuilder() throws Exception {
+        Builder builder = new Builder(this);
+        builder.setBase(getBase());
+
+        for (Jar file : getClasspath()) {
+            builder.addClasspath(file);
+        }
+
+        return builder;
+    }
+
+    /**
+     * A macro to convert a maven version to an OSGi version
+     */
+
+    public String _maven_version(String args[]) {
+        if (args.length > 2)
+            error("${maven_version} macro receives too many arguments "
+                    + Arrays.toString(args));
+        else if (args.length < 2)
+            error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
+        else {
+            return cleanupVersion(args[1]);
+        }
+        return null;
+    }
+
+    public String _permissions(String args[]) throws IOException {
+        StringBuilder sb = new StringBuilder();
+
+        for (String arg : args) {
+            if ("packages".equals(arg) || "all".equals(arg)) {
+                for (String imp : getImports().keySet()) {
+                    if (!imp.startsWith("java.")) {
+                        sb.append("(org.osgi.framework.PackagePermission \"");
+                        sb.append(imp);
+                        sb.append("\" \"import\")\r\n");
+                    }
+                }
+                for (String exp : getExports().keySet()) {
+                    sb.append("(org.osgi.framework.PackagePermission \"");
+                    sb.append(exp);
+                    sb.append("\" \"export\")\r\n");
+                }
+            } else if ("admin".equals(arg) || "all".equals(arg)) {
+                sb.append("(org.osgi.framework.AdminPermission)");
+            } else if ("permissions".equals(arg))
+                ;
+            else
+                error("Invalid option in ${permissions}: %s", arg);
+        }
+        return sb.toString();
+    }
+
+    public void removeBundleSpecificHeaders() {
+        Set<String> set = new HashSet<String>(Arrays
+                .asList(BUNDLE_SPECIFIC_HEADERS));
+        setForceLocal(set);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ClassDataCollector.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ClassDataCollector.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ClassDataCollector.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,14 @@
+package aQute.lib.osgi;
+
+public class ClassDataCollector {
+    public void classBegin(int access, String name) {}
+    public void extendsClass(String name) {}
+    public void implementsInterfaces(String name[]) {}
+    public void field(int access, String descriptor) {}
+    public void constructor(int access, String descriptor) {}
+    public void method(int access, String name, String descriptor) {}
+    public void addReference(String token) {}
+    public void annotation(Annotation annotation) {}
+    public void classEnd() {}
+    public void parameter(int p) {}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Clazz.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Clazz.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Clazz.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1002 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.nio.*;
+import java.util.*;
+
+public class Clazz {
+    public static enum QUERY {
+        IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION, CONCRETE, ABSTRACT, PUBLIC, ANNOTATION, RUNTIMEANNOTATIONS, CLASSANNOTATIONS
+    };
+
+    public static EnumSet<QUERY> HAS_ARGUMENT = EnumSet.of(QUERY.IMPLEMENTS,
+                                                      QUERY.EXTENDS,
+                                                      QUERY.IMPORTS,
+                                                      QUERY.NAMED,
+                                                      QUERY.VERSION,
+                                                      QUERY.ANNOTATION);
+
+    static protected class Assoc {
+        Assoc(byte tag, int a, int b) {
+            this.tag = tag;
+            this.a = a;
+            this.b = b;
+        }
+
+        byte tag;
+        int  a;
+        int  b;
+    }
+
+    final static byte                SkipTable[] = { 0, // 0 non existent
+            -1, // 1 CONSTANT_utf8 UTF 8, handled in
+            // method
+            -1, // 2
+            4, // 3 CONSTANT_Integer
+            4, // 4 CONSTANT_Float
+            8, // 5 CONSTANT_Long (index +=2!)
+            8, // 6 CONSTANT_Double (index +=2!)
+            -1, // 7 CONSTANT_Class
+            2, // 8 CONSTANT_String
+            4, // 9 CONSTANT_FieldRef
+            4, // 10 CONSTANT_MethodRef
+            4, // 11 CONSTANT_InterfaceMethodRef
+            4, // 12 CONSTANT_NameAndType
+                                                 };
+
+    boolean                          isAbstract;
+    boolean                          isPublic;
+    boolean                          hasRuntimeAnnotations;
+    boolean                          hasClassAnnotations;
+
+    String                           className;
+    Object                           pool[];
+    int                              intPool[];
+    Map<String, Map<String, String>> imports     = new HashMap<String, Map<String, String>>();
+    String                           path;
+    int                              minor       = 0;
+    int                              major       = 0;
+
+    String                           sourceFile;
+    Set<String>                      xref;
+    Set<Integer>                     classes;
+    Set<Integer>                     descriptors;
+    Set<String>                      annotations;
+    int                              forName     = 0;
+    int                              class$      = 0;
+    String[]                         interfaces;
+    String                           zuper;
+    ClassDataCollector               cd          = null;
+    Resource                         resource;
+
+    public Clazz(String path, Resource resource) {
+        this.path = path;
+        this.resource = resource;
+    }
+
+    public Set<String> parseClassFile() throws IOException {
+        return parseClassFileWithCollector(null);
+    }
+
+    public Set<String> parseClassFile(InputStream in) throws IOException {
+        return parseClassFile(in, null);
+    }
+
+    public Set<String> parseClassFileWithCollector(ClassDataCollector cd)
+            throws IOException {
+        InputStream in = resource.openInputStream();
+        try {
+            return parseClassFile(in, cd);
+        } finally {
+            in.close();
+        }
+    }
+
+    public Set<String> parseClassFile(InputStream in, ClassDataCollector cd)
+            throws IOException {
+        DataInputStream din = new DataInputStream(in);
+        try {
+            this.cd = cd;
+            return parseClassFile(din);
+        } finally {
+            cd = null;
+            din.close();
+        }
+    }
+
+    Set<String> parseClassFile(DataInputStream in) throws IOException {
+
+        xref = new HashSet<String>();
+        classes = new HashSet<Integer>();
+        descriptors = new HashSet<Integer>();
+
+        boolean crawl = false; // Crawl the byte code
+        int magic = in.readInt();
+        if (magic != 0xCAFEBABE)
+            throw new IOException("Not a valid class file (no CAFEBABE header)");
+
+        minor = in.readUnsignedShort(); // minor version
+        major = in.readUnsignedShort(); // major version
+        int count = in.readUnsignedShort();
+        pool = new Object[count];
+        intPool = new int[count];
+
+        process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
+            byte tag = in.readByte();
+            switch (tag) {
+            case 0:
+                break process;
+            case 1:
+                constantUtf8(in, poolIndex);
+                break;
+
+            case 3:
+                constantInteger(in, poolIndex);
+                break;
+
+            case 4:
+                constantFloat(in, poolIndex);
+                break;
+
+            // For some insane optimization reason are
+            // the long and the double two entries in the
+            // constant pool. See 4.4.5
+            case 5:
+                constantLong(in, poolIndex);
+                poolIndex++;
+                break;
+
+            case 6:
+                constantDouble(in, poolIndex);
+                poolIndex++;
+                break;
+
+            case 7:
+                constantClass(in, poolIndex);
+                break;
+
+            case 8:
+                constantString(in, poolIndex);
+                break;
+
+            case 10: // Method ref
+                methodRef(in, poolIndex);
+                break;
+
+            // Name and Type
+            case 12:
+                nameAndType(in, poolIndex, tag);
+                break;
+
+            // We get the skip count for each record type
+            // from the SkipTable. This will also automatically
+            // abort when
+            default:
+                if (tag == 2)
+                    throw new IOException("Invalid tag " + tag);
+                in.skipBytes(SkipTable[tag]);
+                break;
+            }
+        }
+
+        pool(pool, intPool);
+        /*
+         * Parse after the constant pool, code thanks to Hans Christian
+         * Falkenberg
+         */
+
+        int access_flags = in.readUnsignedShort(); // access
+        isAbstract = Modifier.isAbstract(access_flags);
+        isPublic = Modifier.isPublic(access_flags);
+        int this_class = in.readUnsignedShort();
+        className = (String) pool[intPool[this_class]];
+        if (cd != null)
+            cd.classBegin(access_flags, className);
+
+        try {
+
+            int super_class = in.readUnsignedShort();
+            zuper = (String) pool[intPool[super_class]];
+            if (zuper != null) {
+                String pack = getPackage(zuper);
+                    packageReference(pack);
+                if (cd != null)
+                    cd.extendsClass(zuper);
+            }
+
+            int interfacesCount = in.readUnsignedShort();
+            if (interfacesCount > 0) {
+                interfaces = new String[interfacesCount];
+                for (int i = 0; i < interfacesCount; i++)
+                    interfaces[i] = (String) pool[intPool[in
+                            .readUnsignedShort()]];
+                if (cd != null)
+                    cd.implementsInterfaces(interfaces);
+            }
+
+            int fieldsCount = in.readUnsignedShort();
+            for (int i = 0; i < fieldsCount; i++) {
+                access_flags = in.readUnsignedShort(); // skip access flags
+                int name_index = in.readUnsignedShort();
+                int descriptor_index = in.readUnsignedShort();
+
+                // Java prior to 1.5 used a weird
+                // static variable to hold the com.X.class
+                // result construct. If it did not find it
+                // it would create a variable class$com$X
+                // that would be used to hold the class
+                // object gotten with Class.forName ...
+                // Stupidly, they did not actively use the
+                // class name for the field type, so bnd
+                // would not see a reference. We detect
+                // this case and add an artificial descriptor
+                String name = pool[name_index].toString(); // name_index
+                if (name.startsWith("class$")) {
+                    crawl = true;
+                }
+                if (cd != null)
+                    cd.field(access_flags, pool[descriptor_index].toString());
+                descriptors.add(new Integer(descriptor_index));
+                doAttributes(in, ElementType.FIELD, false);
+            }
+
+            //
+            // Check if we have to crawl the code to find
+            // the ldc(_w) <string constant> invokestatic Class.forName
+            // if so, calculate the method ref index so we
+            // can do this efficiently
+            //
+            if (crawl) {
+                forName = findMethod("java/lang/Class", "forName",
+                        "(Ljava/lang/String;)Ljava/lang/Class;");
+                class$ = findMethod(className, "class$",
+                        "(Ljava/lang/String;)Ljava/lang/Class;");
+            }
+
+            //
+            // Handle the methods
+            //
+            int methodCount = in.readUnsignedShort();
+            for (int i = 0; i < methodCount; i++) {
+                access_flags = in.readUnsignedShort();
+                int name_index = in.readUnsignedShort();
+                int descriptor_index = in.readUnsignedShort();
+                // String s = (String) pool[name_index];
+                descriptors.add(new Integer(descriptor_index));
+                String name = pool[name_index].toString();
+                String descriptor = pool[descriptor_index].toString();
+                if ("<init>".equals(name)) {
+                    if (cd != null)
+                        cd.constructor(access_flags, descriptor);
+                    doAttributes(in, ElementType.CONSTRUCTOR, crawl);
+                } else {
+                    if (cd != null)
+                        cd.method(access_flags, name, descriptor);
+                    doAttributes(in, ElementType.METHOD, crawl);
+                }
+            }
+
+            doAttributes(in, ElementType.TYPE, false);
+
+            //
+            // Now iterate over all classes we found and
+            // parse those as well. We skip duplicates
+            //
+
+            for (int n : classes) {
+                String clazz = (String) pool[n];
+                if (clazz.endsWith(";") || clazz.startsWith("["))
+                    parseReference(clazz, 0);
+                else {
+
+                    String pack = getPackage(clazz);
+                    packageReference(pack);
+                }
+            }
+
+            //
+            // Parse all the descriptors we found
+            //
+
+            for (Iterator<Integer> e = descriptors.iterator(); e.hasNext();) {
+                Integer index = e.next();
+                String prototype = (String) pool[index.intValue()];
+                if (prototype != null)
+                    parseDescriptor(prototype);
+                else
+                    System.err.println("Unrecognized descriptor: " + index);
+            }
+            Set<String> xref = this.xref;
+            reset();
+            return xref;
+        } finally {
+            if (cd != null)
+                cd.classEnd();
+        }
+    }
+
+    private void constantFloat(DataInputStream in, int poolIndex)
+            throws IOException {
+        if (cd != null)
+            pool[poolIndex] = in.readInt();
+        else
+            in.skipBytes(4);
+    }
+
+    private void constantInteger(DataInputStream in, int poolIndex)
+            throws IOException {
+        if (cd != null)
+            intPool[poolIndex] = in.readInt();
+        else
+            in.skipBytes(4);
+    }
+
+    protected void pool(Object[] pool, int[] intPool) {
+    }
+
+    /**
+     * @param in
+     * @param poolIndex
+     * @param tag
+     * @throws IOException
+     */
+    protected void nameAndType(DataInputStream in, int poolIndex, byte tag)
+            throws IOException {
+        int name_index = in.readUnsignedShort();
+        int descriptor_index = in.readUnsignedShort();
+        descriptors.add(new Integer(descriptor_index));
+        pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
+    }
+
+    /**
+     * @param in
+     * @param poolIndex
+     * @param tag
+     * @throws IOException
+     */
+    private void methodRef(DataInputStream in, int poolIndex)
+            throws IOException {
+        int class_index = in.readUnsignedShort();
+        int name_and_type_index = in.readUnsignedShort();
+        pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
+    }
+
+    /**
+     * @param in
+     * @param poolIndex
+     * @throws IOException
+     */
+    private void constantString(DataInputStream in, int poolIndex)
+            throws IOException {
+        int string_index = in.readUnsignedShort();
+        intPool[poolIndex] = string_index;
+    }
+
+    /**
+     * @param in
+     * @param poolIndex
+     * @throws IOException
+     */
+    protected void constantClass(DataInputStream in, int poolIndex)
+            throws IOException {
+        int class_index = in.readUnsignedShort();
+        classes.add(new Integer(class_index));
+        intPool[poolIndex] = class_index;
+    }
+
+    /**
+     * @param in
+     * @throws IOException
+     */
+    protected void constantDouble(DataInputStream in, int poolIndex)
+            throws IOException {
+        if (cd != null)
+            pool[poolIndex] = in.readDouble();
+        else
+            in.skipBytes(8);
+    }
+
+    /**
+     * @param in
+     * @throws IOException
+     */
+    protected void constantLong(DataInputStream in, int poolIndex)
+            throws IOException {
+        if (cd != null) {
+            pool[poolIndex] = in.readLong();
+        } else
+            in.skipBytes(8);
+    }
+
+    /**
+     * @param in
+     * @param poolIndex
+     * @throws IOException
+     */
+    protected void constantUtf8(DataInputStream in, int poolIndex)
+            throws IOException {
+        // CONSTANT_Utf8
+
+        String name = in.readUTF();
+        xref.add(name);
+        pool[poolIndex] = name;
+    }
+
+    /**
+     * Find a method reference in the pool that points to the given class,
+     * methodname and descriptor.
+     * 
+     * @param clazz
+     * @param methodname
+     * @param descriptor
+     * @return index in constant pool
+     */
+    private int findMethod(String clazz, String methodname, String descriptor) {
+        for (int i = 1; i < pool.length; i++) {
+            if (pool[i] instanceof Assoc) {
+                Assoc methodref = (Assoc) pool[i];
+                if (methodref.tag == 10) {
+                    // Method ref
+                    int class_index = methodref.a;
+                    int class_name_index = intPool[class_index];
+                    if (clazz.equals(pool[class_name_index])) {
+                        int name_and_type_index = methodref.b;
+                        Assoc name_and_type = (Assoc) pool[name_and_type_index];
+                        if (name_and_type.tag == 12) {
+                            // Name and Type
+                            int name_index = name_and_type.a;
+                            int type_index = name_and_type.b;
+                            if (methodname.equals(pool[name_index])) {
+                                if (descriptor.equals(pool[type_index])) {
+                                    return i;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Called for each attribute in the class, field, or method.
+     * 
+     * @param in
+     *            The stream
+     * @throws IOException
+     */
+    private void doAttributes(DataInputStream in, ElementType member,
+            boolean crawl) throws IOException {
+        int attributesCount = in.readUnsignedShort();
+        for (int j = 0; j < attributesCount; j++) {
+            // skip name CONSTANT_Utf8 pointer
+            doAttribute(in, member, crawl);
+        }
+    }
+
+    /**
+     * Process a single attribute, if not recognized, skip it.
+     * 
+     * @param in
+     *            the data stream
+     * @throws IOException
+     */
+    private void doAttribute(DataInputStream in, ElementType member,
+            boolean crawl) throws IOException {
+        int attribute_name_index = in.readUnsignedShort();
+        String attributeName = (String) pool[attribute_name_index];
+        long attribute_length = in.readInt();
+        attribute_length &= 0xFFFFFFFF;
+        if ("RuntimeVisibleAnnotations".equals(attributeName))
+            doAnnotations(in, member, RetentionPolicy.RUNTIME);
+        else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
+            doParameterAnnotations(in, member, RetentionPolicy.RUNTIME);
+        else if ("RuntimeInvisibleAnnotations".equals(attributeName))
+            doAnnotations(in, member, RetentionPolicy.CLASS);
+        else if ("RuntimeInvisibleParameterAnnotations".equals(attributeName))
+            doParameterAnnotations(in, member, RetentionPolicy.CLASS);
+        else if ("SourceFile".equals(attributeName))
+            doSourceFile(in);
+        else if ("Code".equals(attributeName) && crawl)
+            doCode(in);
+        else if ("Signature".equals(attributeName))
+            doSignature(in, member);
+        else {
+            if (attribute_length > 0x7FFFFFFF) {
+                throw new IllegalArgumentException("Attribute > 2Gb");
+            }
+            in.skipBytes((int) attribute_length);
+        }
+    }
+
+    /**
+     * Handle a signature
+     * 
+     * <pre>
+     * Signature_attribute { 
+     *     u2 attribute_name_index; 
+     *     u4 attribute_length; 
+     *     u2 signature_index; 
+     *     } 
+     * </pre>
+     * 
+     * @param member
+     */
+
+    void doSignature(DataInputStream in, ElementType member) throws IOException {
+        int signature_index = in.readUnsignedShort();
+        String signature = (String) pool[signature_index];
+
+        // The type signature is kind of weird,
+        // lets skip it for now. Seems to be some kind of
+        // type variable name index but it does not seem to
+        // conform to the language specification.
+        if (member != ElementType.TYPE)
+            parseDescriptor(signature);
+    }
+
+    /**
+     * <pre>
+     * Code_attribute {
+     * 		u2 attribute_name_index;
+     * 		u4 attribute_length;
+     * 		u2 max_stack;
+     * 		u2 max_locals;
+     * 		u4 code_length;
+     * 		u1 code[code_length];
+     * 		u2 exception_table_length;
+     * 		{    	u2 start_pc;
+     * 		      	u2 end_pc;
+     * 		      	u2  handler_pc;
+     * 		      	u2  catch_type;
+     * 		}	exception_table[exception_table_length];
+     * 		u2 attributes_count;
+     * 		attribute_info attributes[attributes_count];
+     * 	}
+     * </pre>
+     * 
+     * @param in
+     * @param pool
+     * @throws IOException
+     */
+    private void doCode(DataInputStream in) throws IOException {
+        /* int max_stack = */in.readUnsignedShort();
+        /* int max_locals = */in.readUnsignedShort();
+        int code_length = in.readInt();
+        byte code[] = new byte[code_length];
+        in.readFully(code);
+        crawl(code);
+        int exception_table_length = in.readUnsignedShort();
+        in.skipBytes(exception_table_length * 8);
+        doAttributes(in, ElementType.METHOD, false);
+    }
+
+    /**
+     * We must find Class.forName references ...
+     * 
+     * @param code
+     */
+    protected void crawl(byte[] code) {
+        ByteBuffer bb = ByteBuffer.wrap(code);
+        bb.order(ByteOrder.BIG_ENDIAN);
+        int lastReference = -1;
+
+        while (bb.remaining() > 0) {
+            int instruction = 0xFF & bb.get();
+            switch (instruction) {
+            case OpCodes.ldc:
+                lastReference = 0xFF & bb.get();
+                break;
+
+            case OpCodes.ldc_w:
+                lastReference = 0xFFFF & bb.getShort();
+                break;
+
+            case OpCodes.invokestatic:
+                int methodref = 0xFFFF & bb.getShort();
+                if ((methodref == forName || methodref == class$)
+                        && lastReference != -1
+                        && pool[intPool[lastReference]] instanceof String) {
+                    String clazz = (String) pool[intPool[lastReference]];
+                    if (clazz.startsWith("[") || clazz.endsWith(";"))
+                        parseReference(clazz, 0);
+                    else {
+                        int n = clazz.lastIndexOf('.');
+                        if (n > 0 )
+                            packageReference(clazz.substring(0, n));
+                    }
+                }
+                break;
+
+            case OpCodes.tableswitch:
+                // Skip to place divisible by 4
+                while ((bb.position() & 0x3) != 0)
+                    bb.get();
+                /* int deflt = */
+                bb.getInt();
+                int low = bb.getInt();
+                int high = bb.getInt();
+                bb.position(bb.position() + (high - low + 1) * 4);
+                lastReference = -1;
+                break;
+
+            case OpCodes.lookupswitch:
+                // Skip to place divisible by 4
+                while ((bb.position() & 0x3) != 0)
+                    bb.get();
+                /* deflt = */
+                bb.getInt();
+                int npairs = bb.getInt();
+                bb.position(bb.position() + npairs * 8);
+                lastReference = -1;
+                break;
+
+            default:
+                lastReference = -1;
+                bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
+            }
+        }
+    }
+
+    private void doSourceFile(DataInputStream in) throws IOException {
+        int sourcefile_index = in.readUnsignedShort();
+        this.sourceFile = pool[sourcefile_index].toString();
+    }
+
+    private void doParameterAnnotations(DataInputStream in, ElementType member,
+            RetentionPolicy policy) throws IOException {
+        int num_parameters = in.readUnsignedByte();
+        for (int p = 0; p < num_parameters; p++) {
+            if (cd != null)
+                cd.parameter(p);
+            doAnnotations(in, member, policy);
+        }
+    }
+
+    private void doAnnotations(DataInputStream in, ElementType member,
+            RetentionPolicy policy) throws IOException {
+        int num_annotations = in.readUnsignedShort(); // # of annotations
+        for (int a = 0; a < num_annotations; a++) {
+            if (cd == null)
+                doAnnotation(in, member, policy, false);
+            else {
+                Annotation annotion = doAnnotation(in, member, policy, true);
+                cd.annotation(annotion);
+            }
+        }
+    }
+
+    private Annotation doAnnotation(DataInputStream in, ElementType member,
+            RetentionPolicy policy, boolean collect) throws IOException {
+        int type_index = in.readUnsignedShort();
+        if (annotations == null)
+            annotations = new HashSet<String>();
+
+        annotations.add(pool[type_index].toString());
+
+        if (policy == RetentionPolicy.RUNTIME) {
+            descriptors.add(new Integer(type_index));
+            hasRuntimeAnnotations = true;
+        } else {
+            hasClassAnnotations = true;
+        }
+        String name = (String) pool[type_index];
+        int num_element_value_pairs = in.readUnsignedShort();
+        Map<String, Object> elements = null;
+        for (int v = 0; v < num_element_value_pairs; v++) {
+            int element_name_index = in.readUnsignedShort();
+            String element = (String) pool[element_name_index];
+            Object value = doElementValue(in, member, policy, collect);
+            if (collect) {
+                if (elements == null)
+                    elements = new LinkedHashMap<String, Object>();
+                elements.put(element, value);
+            }
+        }
+        if (collect)
+            return new Annotation(name, elements, member, policy);
+        else
+            return null;
+    }
+
+    private Object doElementValue(DataInputStream in, ElementType member,
+            RetentionPolicy policy, boolean collect) throws IOException {
+        char tag = (char) in.readUnsignedByte();
+        switch (tag) {
+        case 'B': // Byte
+        case 'C': // Character
+        case 'I': // Integer
+        case 'S': // Short
+            int const_value_index = in.readUnsignedShort();
+            return intPool[const_value_index];
+
+        case 'D': // Double
+        case 'F': // Float
+        case 's': // String
+        case 'J': // Long
+            const_value_index = in.readUnsignedShort();
+            return pool[const_value_index];
+
+        case 'Z': // Boolean
+            const_value_index = in.readUnsignedShort();
+            return intPool[const_value_index] == 0 ? false : true;
+
+        case 'e': // enum constant
+            int type_name_index = in.readUnsignedShort();
+            if (policy == RetentionPolicy.RUNTIME)
+                descriptors.add(new Integer(type_name_index));
+            int const_name_index = in.readUnsignedShort();
+            return pool[const_name_index];
+
+        case 'c': // Class
+            int class_info_index = in.readUnsignedShort();
+            if (policy == RetentionPolicy.RUNTIME)
+                descriptors.add(new Integer(class_info_index));
+            return pool[class_info_index];
+
+        case '@': // Annotation type
+            return doAnnotation(in, member, policy, collect);
+
+        case '[': // Array
+            int num_values = in.readUnsignedShort();
+            Object[] result = new Object[num_values];
+            for (int i = 0; i < num_values; i++) {
+                result[i] = doElementValue(in, member, policy, collect);
+            }
+            return result;
+
+        default:
+            throw new IllegalArgumentException(
+                    "Invalid value for Annotation ElementValue tag " + tag);
+        }
+    }
+
+    /**
+     * Add a new package reference.
+     * 
+     * @param pack
+     *            A '.' delimited package name
+     */
+    void packageReference(String pack) {
+        if (!imports.containsKey(pack))
+            imports.put(pack, new LinkedHashMap<String, String>());
+    }
+
+    /**
+     * This method parses a descriptor and adds the package of the descriptor to
+     * the referenced packages.
+     * 
+     * The syntax of the descriptor is:
+     * 
+     * <pre>
+     *   descriptor ::= ( '(' references ')' )? references
+     *   references ::= reference *
+     *   reference  ::= 'L' classname ( '&lt;' references '&gt;' )? ';' | 'B' | 'Z' | ... | '+' | '-' | '[' 
+     * </pre>
+     * 
+     * This methods uses heavy recursion to parse the descriptor and a roving
+     * pointer to limit the creation of string objects.
+     * 
+     * @param descriptor
+     *            The to be parsed descriptor
+     * @param rover
+     *            The pointer to start at
+     */
+    public void parseDescriptor(String descriptor) {
+        // Some descriptors are weird, they start with a generic
+        // declaration that contains ':', not sure what they mean ...
+        if (descriptor.charAt(0) == '<')
+            return;
+
+        int rover = 0;
+        if (descriptor.charAt(rover) == '(') {
+            rover = parseReferences(descriptor, rover + 1, ')');
+            rover++;
+        }
+        parseReferences(descriptor, rover, (char) 0);
+    }
+
+    /**
+     * Parse a sequence of references. A sequence ends with a given character or
+     * when the string ends.
+     * 
+     * @param descriptor
+     *            The whole descriptor.
+     * @param rover
+     *            The index in the descriptor
+     * @param delimiter
+     *            The end character or 0
+     * @return the last index processed, one character after the delimeter
+     */
+    int parseReferences(String descriptor, int rover, char delimiter) {
+        while (rover < descriptor.length()
+                && descriptor.charAt(rover) != delimiter) {
+            rover = parseReference(descriptor, rover);
+        }
+        return rover;
+    }
+
+    /**
+     * Parse a single reference. This can be a single character or an object
+     * reference when it starts with 'L'.
+     * 
+     * @param descriptor
+     *            The descriptor
+     * @param rover
+     *            The place to start
+     * @return The return index after the reference
+     */
+    int parseReference(String descriptor, int rover) {
+
+        char c = descriptor.charAt(rover);
+        while (c == '[')
+            c = descriptor.charAt(++rover);
+
+        if (c == '<') {
+            rover = parseReferences(descriptor, rover + 1, '>');
+        } else if (c == 'T') {
+            // Type variable name
+            rover++;
+            while (descriptor.charAt(rover) != ';')
+                rover++;
+        } else if (c == 'L') {
+            StringBuilder sb = new StringBuilder();
+            rover++;
+            int lastSlash = -1;
+            while ((c = descriptor.charAt(rover)) != ';') {
+                if (c == '<') {
+                    rover = parseReferences(descriptor, rover + 1, '>');
+                } else if (c == '/') {
+                    lastSlash = sb.length();
+                    sb.append('.');
+                } else
+                    sb.append(c);
+                rover++;
+            }
+            if (cd != null)
+                cd.addReference(sb.toString());
+
+            if (lastSlash > 0)
+                packageReference(sb.substring(0, lastSlash));
+        } else {
+            if ("+-*BCDFIJSZV".indexOf(c) < 0)
+                System.out.println("Should not skip: " + c);
+        }
+
+        // this skips a lot of characters
+        // [, *, +, -, B, etc.
+
+        return rover + 1;
+    }
+
+    public static String getPackage(String clazz) {
+        int n = clazz.lastIndexOf('/');
+        if (n < 0)
+            return ".";
+        return clazz.substring(0, n).replace('/', '.');
+    }
+
+    public Map<String, Map<String, String>> getReferred() {
+        return imports;
+    }
+
+    String getClassName() {
+        return className;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getSourceFile() {
+        return sourceFile;
+    }
+
+    /**
+     * .class construct for different compilers
+     * 
+     * sun 1.1 Detect static variable class$com$acme$MyClass 1.2 " 1.3 " 1.4 "
+     * 1.5 ldc_w (class) 1.6 "
+     * 
+     * eclipse 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 "
+     * 1.5 ldc (class) 1.6 "
+     * 
+     * 1.5 and later is not an issue, sun pre 1.5 is easy to detect the static
+     * variable that decodes the class name. For eclipse, the class$0 gives away
+     * we have a reference encoded in a string.
+     * compilerversions/compilerversions.jar contains test versions of all
+     * versions/compilers.
+     */
+
+    public void reset() {
+        pool = null;
+        intPool = null;
+        xref = null;
+        classes = null;
+        descriptors = null;
+    }
+
+    public boolean is(QUERY query, Instruction instr,
+            Map<String, Clazz> classspace) {
+        switch (query) {
+        case ANY:
+            return true;
+
+        case NAMED:
+            if (instr.matches(getClassName()))
+                return !instr.isNegated();
+            return false;
+
+        case VERSION:
+            String v = major + "/" + minor;
+            if (instr.matches(v))
+                return !instr.isNegated();
+            return false;
+
+        case IMPLEMENTS:
+            for (int i = 0; interfaces != null && i < interfaces.length; i++) {
+                if (instr.matches(interfaces[i]))
+                    return !instr.isNegated();
+            }
+            break;
+        case EXTENDS:
+            if (zuper == null)
+                return false;
+
+            if (instr.matches(zuper))
+                return !instr.isNegated();
+            break;
+
+        case PUBLIC:
+            return !isPublic;
+
+        case CONCRETE:
+            return !isAbstract;
+
+        case ANNOTATION:
+            if (annotations == null)
+                return false;
+
+            for (String annotation : annotations) {
+                if (instr.matches(annotation))
+                    return !instr.isNegated();
+            }
+
+            return false;
+
+        case RUNTIMEANNOTATIONS:
+            return hasClassAnnotations;
+        case CLASSANNOTATIONS:
+            return hasClassAnnotations;
+
+        case ABSTRACT:
+            return isAbstract;
+
+        case IMPORTS:
+            for (String imp : imports.keySet()) {
+                if (instr.matches(imp.replace('.', '/')))
+                    return !instr.isNegated();
+            }
+        }
+
+        if (zuper == null || classspace == null)
+            return false;
+
+        Clazz clazz = classspace.get(zuper + ".class");
+        if (clazz == null)
+            return false;
+
+        return clazz.is(query, instr, classspace);
+    }
+
+    public String toString() {
+        return getFQN();
+    }
+
+    public String getFQN() {
+        return getClassName().replace('/', '.');
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Constants.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Constants.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Constants.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,200 @@
+package aQute.lib.osgi;
+
+import java.util.regex.*;
+
+public interface Constants {
+    /*
+     * Defined in OSGi
+     */
+    /**
+     * @syntax Bundle-ActivationPolicy ::= policy ( ’;’ directive )* policy ::=
+     *         ’lazy’
+     */
+    String               BUNDLE_ACTIVATIONPOLICY                   = "Bundle-ActivationPolicy";
+    String               BUNDLE_ACTIVATOR                          = "Bundle-Activator";
+    String               BUNDLE_BLUEPRINT                          = "Bundle-Copyright";
+    String               BUNDLE_CATEGORY                           = "Bundle-Category";
+    String               BUNDLE_CLASSPATH                          = "Bundle-ClassPath";
+    String               BUNDLE_CONTACTADDRESS                     = "Bundle-ContactAddress";
+    String               BUNDLE_COPYRIGHT                          = "Bundle-Copyright";
+    String               BUNDLE_DESCRIPTION                        = "Bundle-Description";
+    String               BUNDLE_DOCURL                             = "Bundle-DocURL";
+    String               BUNDLE_ICON                               = "Bundle-Icon";
+    String               BUNDLE_LICENSE                            = "Bundle-License";
+    String               BUNDLE_LOCALIZATION                       = "Bundle-Localization";
+    String               BUNDLE_MANIFESTVERSION                    = "Bundle-ManifestVersion";
+    String               BUNDLE_NAME                               = "Bundle-Name";
+    String               BUNDLE_NATIVECODE                         = "Bundle-NativeCode";
+    String               BUNDLE_REQUIREDEXECUTIONENVIRONMENT       = "Bundle-RequiredExecutionEnvironment";
+    String               BUNDLE_SYMBOLICNAME                       = "Bundle-SymbolicName";
+    String               BUNDLE_UPDATELOCATION                     = "Bundle-UpdateLocation";
+    String               BUNDLE_VENDOR                             = "Bundle-Vendor";
+    String               BUNDLE_VERSION                            = "Bundle-Version";
+    String               DYNAMICIMPORT_PACKAGE                     = "DynamicImport-Package";
+    String               EXPORT_PACKAGE                            = "Export-Package";
+    String               EXPORT_SERVICE                            = "Export-Service";
+    String               FRAGMENT_HOST                             = "Fragment-Host";
+    String               IMPORT_PACKAGE                            = "Import-Package";
+    String               IMPORT_SERVICE                            = "Import-Service";
+    String               REQUIRE_BUNDLE                            = "Require-Bundle";
+    String               SERVICE_COMPONENT                         = "Service-Component";
+
+    String               PRIVATE_PACKAGE                           = "Private-Package";
+    String               IGNORE_PACKAGE                            = "Ignore-Package";
+    String               INCLUDE_RESOURCE                          = "Include-Resource";
+    String               CONDITIONAL_PACKAGE                       = "Conditional-Package";
+    String               BND_LASTMODIFIED                          = "Bnd-LastModified";
+    String               CREATED_BY                                = "Created-By";
+    String               TOOL                                      = "Tool";
+    String               TESTCASES                                 = "Test-Cases";
+    String               SIGNATURE_TEST                            = "-signaturetest";
+
+    String               headers[]                                 = {
+            BUNDLE_ACTIVATOR, BUNDLE_CONTACTADDRESS, BUNDLE_COPYRIGHT,
+            BUNDLE_DESCRIPTION, BUNDLE_DOCURL, BUNDLE_LOCALIZATION,
+            BUNDLE_NATIVECODE, BUNDLE_VENDOR, BUNDLE_VERSION, BUNDLE_LICENSE,
+            BUNDLE_CLASSPATH, SERVICE_COMPONENT, EXPORT_PACKAGE,
+            IMPORT_PACKAGE, BUNDLE_LOCALIZATION, BUNDLE_MANIFESTVERSION,
+            BUNDLE_NAME, BUNDLE_NATIVECODE,
+            BUNDLE_REQUIREDEXECUTIONENVIRONMENT, BUNDLE_SYMBOLICNAME,
+            BUNDLE_VERSION, FRAGMENT_HOST, PRIVATE_PACKAGE, IGNORE_PACKAGE,
+            INCLUDE_RESOURCE, REQUIRE_BUNDLE, IMPORT_SERVICE, EXPORT_SERVICE,
+            CONDITIONAL_PACKAGE, BND_LASTMODIFIED, TESTCASES, SIGNATURE_TEST };
+
+    String               BUILDPATH                                 = "-buildpath";
+    String               BUMPPOLICY                                = "-bumppolicy";
+    String               CONDUIT                                   = "-conduit";
+    String               DEPENDSON                                 = "-dependson";
+    String               DEPLOYREPO                                = "-deployrepo";
+    String               DONOTCOPY                                 = "-donotcopy";
+    String               DEBUG                                     = "-debug";
+    String               EXPORT_CONTENTS                           = "-exportcontents";
+    String               FAIL_OK                                   = "-failok";
+    String               INCLUDE                                   = "-include";
+    String               INCLUDERESOURCE                           = "-includeresource";
+    String               MAKE                                      = "-make";
+    String               MANIFEST                                  = "-manifest";
+    String               NOEXTRAHEADERS                            = "-noextraheaders";
+    String               NOMANIFEST                                = "-nomanifest";
+    String               NOUSES                                    = "-nouses";
+    String               NOPE                                      = "-nope";
+    String               PEDANTIC                                  = "-pedantic";
+    String               PLUGIN                                    = "-plugin";
+    String               POM                                       = "-pom";
+    String               RELEASEREPO                               = "-releaserepo";
+    String               REMOVE_HEADERS                            = "-removeheaders";
+    String               RESOURCEONLY                              = "-resourceonly";
+    String               SOURCES                                   = "-sources";
+    String               SOURCEPATH                                = "-sourcepath";
+    String               SUB                                       = "-sub";
+    String               RUNPROPERTIES                             = "-runproperties";
+    String               RUNSYSTEMPACKAGES                         = "-runsystempackages";
+    String               RUNBUNDLES                                = "-runbundles";
+    String               RUNPATH                                   = "-runpath";
+    String               RUNVM                                     = "-runvm";
+
+    String               REPORTNEWER                               = "-reportnewer";
+    String               SIGN                                      = "-sign";
+    String               TESTPACKAGES                              = "-testpackages";
+    String               TESTREPORT                                = "-testreport";
+    String               TESTBUNDLES                               = "-testbundles";
+    String               UNDERTEST                                 = "-undertest";
+    String               VERBOSE                                   = "-verbose";
+    String               VERSIONPOLICY                             = "-versionpolicy";
+
+    // Deprecated
+    String               CLASSPATH                                 = "-classpath";
+
+    String               options[]                                 = {
+            BUILDPATH, BUMPPOLICY, CONDUIT, CLASSPATH, DEPENDSON, DONOTCOPY,
+            EXPORT_CONTENTS, FAIL_OK, INCLUDE, INCLUDERESOURCE, MAKE, MANIFEST,
+            NOEXTRAHEADERS, NOUSES, NOPE, PEDANTIC, PLUGIN, POM,
+            REMOVE_HEADERS, RESOURCEONLY, SOURCES, SOURCEPATH, SOURCES,
+            SOURCEPATH, SUB, RUNBUNDLES, RUNPATH, RUNSYSTEMPACKAGES,
+            RUNPROPERTIES, REPORTNEWER, UNDERTEST, TESTBUNDLES, TESTPACKAGES,
+            TESTREPORT, VERBOSE, NOMANIFEST, DEPLOYREPO, RELEASEREPO };
+
+    // Ignore bundle specific headers. These bundles do not make
+    // a lot of sense to inherit
+    String[]             BUNDLE_SPECIFIC_HEADERS                   = new String[] {
+            INCLUDE_RESOURCE, BUNDLE_ACTIVATOR, BUNDLE_CLASSPATH, BUNDLE_NAME,
+            BUNDLE_NATIVECODE, BUNDLE_SYMBOLICNAME, IMPORT_PACKAGE,
+            EXPORT_PACKAGE, DYNAMICIMPORT_PACKAGE, FRAGMENT_HOST,
+            REQUIRE_BUNDLE, PRIVATE_PACKAGE, EXPORT_CONTENTS, TESTCASES,
+            NOMANIFEST, SIGNATURE_TEST                            };
+
+    char                 DUPLICATE_MARKER                          = '~';
+
+    String               SPLIT_PACKAGE_DIRECTIVE                   = "-split-package:";
+    String               IMPORT_DIRECTIVE                          = "-import:";
+    String               NO_IMPORT_DIRECTIVE                       = "-noimport:";
+    String               REMOVE_ATTRIBUTE_DIRECTIVE                = "-remove-attribute:";
+
+    String               COMMAND_DIRECTIVE                         = "command:";
+    String               USES_DIRECTIVE                            = "uses:";
+    String               MANDATORY_DIRECTIVE                       = "mandatory:";
+    String               INCLUDE_DIRECTIVE                         = "include:";
+    String               EXCLUDE_DIRECTIVE                         = "exclude:";
+    String               PRESENCE_DIRECTIVE                        = "presence:";
+    String               SINGLETON_DIRECTIVE                       = "singleton:";
+    String               EXTENSION_DIRECTIVE                       = "extension:";
+    String               VISIBILITY_DIRECTIVE                      = "visibility:";
+    String               FRAGMENT_ATTACHMENT_DIRECTIVE             = "fragment-attachment:";
+    String               RESOLUTION_DIRECTIVE                      = "resolution:";
+    String               PATH_DIRECTIVE                            = "path:";
+    String               SIZE_ATTRIBUTE                            = "size";
+    String               LINK_ATTRIBUTE                            = "link";
+    String               NAME_ATTRIBUTE                            = "name";
+    String               DESCRIPTION_ATTRIBUTE                     = "description";
+    String               OSNAME_ATTRIBUTE                          = "osname";
+    String               OSVERSION_ATTRIBUTE                       = "osversion";
+    String               PROCESSOR_ATTRIBUTE                       = "processor";
+    String               LANGUAGE_ATTRIBUTE                        = "language";
+    String               SELECTION_FILTER_ATTRIBUTE                = "selection-filter";
+    String               BLUEPRINT_WAIT_FOR_DEPENDENCIES_ATTRIBUTE = "blueprint.wait-for-dependencies";
+    String               BLUEPRINT_TIMEOUT_ATTRIBUTE               = "blueprint.timeout";
+    String               VERSION_ATTRIBUTE                         = "version";
+    String               BUNDLE_SYMBOLIC_NAME_ATTRIBUTE            = "bundle-symbolic-name";
+    String               BUNDLE_VERSION_ATTRIBUTE                  = "bundle-version";
+
+    String               KEYSTORE_LOCATION_DIRECTIVE               = "keystore:";
+    String               KEYSTORE_PROVIDER_DIRECTIVE               = "provider:";
+    String               KEYSTORE_PASSWORD_DIRECTIVE               = "password:";
+    String               SIGN_PASSWORD_DIRECTIVE                   = "sign-password:";
+
+    String               NONE                                      = "none";
+
+    String               directives[]                              = {
+            SPLIT_PACKAGE_DIRECTIVE, NO_IMPORT_DIRECTIVE, IMPORT_DIRECTIVE,
+            RESOLUTION_DIRECTIVE, INCLUDE_DIRECTIVE, USES_DIRECTIVE,
+            EXCLUDE_DIRECTIVE, KEYSTORE_LOCATION_DIRECTIVE,
+            KEYSTORE_PROVIDER_DIRECTIVE, KEYSTORE_PASSWORD_DIRECTIVE,
+            SIGN_PASSWORD_DIRECTIVE, COMMAND_DIRECTIVE
+
+                                                                   // TODO
+                                                                   };
+
+    String               USES_USES                                 = "<<USES>>";
+    String               CURRENT_USES                              = "@uses";
+    String               IMPORT_REFERENCE                          = "reference";
+    String               IMPORT_PRIVATE                            = "private";
+    String[]             importDirectives                          = {
+            IMPORT_REFERENCE, IMPORT_PRIVATE                      };
+
+    static final Pattern VALID_PROPERTY_TYPES                      = Pattern
+                                                                           .compile("(String|Long|Double|Float|Integer|Byte|Character|Boolean|Short)");
+
+    String               DEFAULT_BND_EXTENSION                     = ".bnd";
+    String               DEFAULT_JAR_EXTENSION                     = ".jar";
+    String               DEFAULT_BAR_EXTENSION                     = ".bar";
+    String[]             METAPACKAGES                              = {
+            "META-INF", "OSGI-INF", "OSGI-OPT"                    };
+
+    int                  STRATEGY_HIGHEST                          = 1;
+    int                  STRATEGY_LOWEST                           = -1;
+
+    String               CURRENT_VERSION                           = "@";
+    String               CURRENT_PACKAGE                           = "@package";
+
+    String               BUILDFILES                                = "buildfiles";
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/EmbeddedResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/EmbeddedResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/EmbeddedResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,86 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.zip.*;
+
+public class EmbeddedResource implements Resource {
+	byte	data[];
+	long 	lastModified;
+	String	extra;
+
+	public EmbeddedResource(byte data[], long lastModified) {
+		this.data = data;
+		this.lastModified = lastModified;
+	}
+
+	public InputStream openInputStream() throws FileNotFoundException {
+		return new ByteArrayInputStream(data);
+	}
+
+	public void write(OutputStream out) throws IOException {
+		out.write(data);
+	}
+
+	public String toString() {
+		return ":" + data.length + ":";
+	}
+
+	public static void build(Jar jar, InputStream in, long lastModified) throws IOException {
+		ZipInputStream jin = new ZipInputStream(in);
+		ZipEntry entry = jin.getNextEntry();
+		while (entry != null) {
+			if (!entry.isDirectory()) {
+				byte data[] = collect(jin);
+				jar.putResource(entry.getName(), new EmbeddedResource(data, lastModified), true);
+			}
+			entry = jin.getNextEntry();
+		}
+		jin.close();
+	}
+
+	/**
+	 * Convenience method to turn an inputstream into a byte array. The method
+	 * uses a recursive algorithm to minimize memory usage.
+	 * 
+	 * @param in stream with data
+	 * @param offset where we are in the stream
+	 * @returns byte array filled with data
+	 */
+    static byte[] collect(InputStream in) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        copy(in,out);
+        return out.toByteArray();
+    }
+
+    static void copy(InputStream in, OutputStream out) throws IOException {
+        int available = in.available();
+        if ( available <= 10000)
+            available = 64000;
+        byte [] buffer = new byte[available];
+        int size;
+        while ( (size=in.read(buffer))>0)
+            out.write(buffer,0,size);
+    }
+
+	public long lastModified() {
+		return lastModified;
+	}
+
+	public static void build(Jar sub, Resource resource) throws IOException {
+			InputStream in = resource.openInputStream();
+			build(sub,in, resource.lastModified());
+			in.close();
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+
+	public long size() {
+	    return data.length;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/FileResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/FileResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/FileResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,84 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.regex.Pattern;
+
+public class FileResource implements Resource {
+	File	file;
+	String	extra;
+	
+	public FileResource(File file) {
+		this.file = file;
+	}
+
+	public InputStream openInputStream() throws FileNotFoundException {
+		return new FileInputStream(file);
+	}
+
+	public static void build(Jar jar, File directory, Pattern doNotCopy) {
+		traverse(
+				jar,
+				directory.getAbsolutePath().length(),
+				directory,
+				doNotCopy);
+	}
+
+	public String toString() {
+		return ":" + file.getName() + ":";
+	}
+
+	public void write(OutputStream out) throws IOException {
+		copy(this, out);
+	}
+
+	static synchronized void copy(Resource resource, OutputStream out)
+			throws IOException {
+		InputStream in = resource.openInputStream();
+		try {
+			byte buffer[] = new byte[20000];
+			int size = in.read(buffer);
+			while (size > 0) {
+				out.write(buffer, 0, size);
+				size = in.read(buffer);
+			}
+		}
+		finally {
+			in.close();
+		}
+	}
+
+	static void traverse(Jar jar, int rootlength, File directory,
+			Pattern doNotCopy) {
+		if (doNotCopy.matcher(directory.getName()).matches())
+			return;
+
+		File files[] = directory.listFiles();
+		for (int i = 0; i < files.length; i++) {
+			if (files[i].isDirectory())
+				traverse(jar, rootlength, files[i], doNotCopy);
+			else {
+				String path = files[i].getAbsolutePath().substring(
+						rootlength + 1);
+				if (File.separatorChar != '/')
+					path = path.replace(File.separatorChar, '/');
+				jar.putResource(path, new FileResource(files[i]), true);
+			}
+		}
+	}
+
+	public long lastModified() {
+		return file.lastModified();
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+	
+	public long size() {
+	    return (int) file.length();
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Instruction.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Instruction.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Instruction.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,95 @@
+package aQute.lib.osgi;
+
+import java.util.regex.*;
+
+public class Instruction {
+    Pattern pattern;
+    String  instruction;
+    boolean negated;
+    boolean optional;
+    
+    public Instruction(String instruction, boolean negated) {
+        this.instruction = instruction;
+        this.negated = negated;
+    }
+
+    public boolean matches(String value) {
+        return getMatcher(value).matches();
+    }
+
+    public boolean isNegated() {
+        return negated;
+    }
+
+    public String getPattern() {
+        return instruction;
+    }
+
+    /**
+     * Convert a string based pattern to a regular expression based pattern.
+     * This is called an instruction, this object makes it easier to handle the
+     * different cases
+     * 
+     * @param string
+     * @return
+     */
+    public static Instruction getPattern(String string) {
+        boolean negated = false;
+        if (string.startsWith("!")) {
+            negated = true;
+            string = string.substring(1);
+        }
+        StringBuffer sb = new StringBuffer();
+        for (int c = 0; c < string.length(); c++) {
+            switch (string.charAt(c)) {
+            case '.':
+                sb.append("\\.");
+                break;
+            case '*':
+                sb.append(".*");
+                break;
+            case '?':
+                sb.append(".?");
+                break;
+            default:
+                sb.append(string.charAt(c));
+                break;
+            }
+        }
+        string = sb.toString();
+        if (string.endsWith("\\..*")) {
+            sb.append("|");
+            sb.append(string.substring(0, string.length() - 4));
+        }
+        return new Instruction(sb.toString(), negated);
+    }
+
+    public String toString() {
+        return getPattern();
+    }
+
+    public Matcher getMatcher(String value) {
+        if (pattern == null) {
+            pattern = Pattern.compile(instruction);
+        }
+        return pattern.matcher(value);
+    }
+
+    public int hashCode() {
+        return instruction.hashCode();
+    }
+
+    public boolean equals(Object other) {
+        return other != null && (other instanceof Instruction)
+                && instruction.equals(((Instruction) other).instruction);
+    }
+
+    public void setOptional() {
+        optional = true;
+    }
+    
+    public boolean isOptional() {
+        return optional;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/InstructionFilter.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/InstructionFilter.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/InstructionFilter.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,32 @@
+package aQute.lib.osgi;
+
+import java.io.File;
+import java.io.FileFilter;
+
+public class InstructionFilter implements FileFilter {
+
+	private Instruction instruction;
+	private boolean recursive;
+	
+	public InstructionFilter (Instruction instruction, boolean recursive) {
+		this.instruction = instruction;
+		this.recursive = recursive;
+	}
+	public boolean isRecursive() {
+		return recursive;
+	}
+	public boolean accept(File pathname) {
+		if (Analyzer.doNotCopy.matcher(pathname.getName()).matches()) {
+			return false;
+		}
+
+		if (pathname.isDirectory() && isRecursive()) {
+			return true;
+		}
+		
+		if (instruction == null) {
+			return true;
+		}
+		return !instruction.isNegated() & instruction.matches(pathname.getName());
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Jar.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Jar.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Jar.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,427 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+import java.util.zip.*;
+
+import aQute.libg.reporter.*;
+
+public class Jar implements Closeable {
+    public static final Object[]       EMPTY_ARRAY = new Jar[0];
+    Map<String, Resource>              resources   = new TreeMap<String, Resource>();
+    Map<String, Map<String, Resource>> directories = new TreeMap<String, Map<String, Resource>>();
+    Manifest                           manifest;
+    boolean                            manifestFirst;
+    String                             name;
+    File                               source;
+    ZipFile                            zipFile;
+    long                               lastModified;
+    String                             lastModifiedReason;
+    Reporter                           reporter;
+    boolean                            doNotTouchManifest;
+    boolean                            nomanifest;
+
+    public Jar(String name) {
+        this.name = name;
+    }
+
+    public Jar(String name, File dirOrFile) throws ZipException, IOException {
+        this(name);
+        source = dirOrFile;
+        if (dirOrFile.isDirectory())
+            FileResource.build(this, dirOrFile, Analyzer.doNotCopy);
+        else {
+            zipFile = ZipResource.build(this, dirOrFile);
+        }
+    }
+
+    public Jar(String name, InputStream in, long lastModified)
+            throws IOException {
+        this(name);
+        EmbeddedResource.build(this, in, lastModified);
+    }
+
+    public Jar(String name, String path) throws IOException {
+        this(name);
+        File f = new File(path);
+        InputStream in = new FileInputStream(f);
+        EmbeddedResource.build(this, in, f.lastModified());
+        in.close();
+    }
+
+    public Jar(File jar) throws IOException {
+        this(getName(jar), jar);
+    }
+
+    /**
+     * Make the JAR file name the project name if we get a src or bin directory.
+     * 
+     * @param f
+     * @return
+     */
+    private static String getName(File f) {
+        f = f.getAbsoluteFile();
+        String name = f.getName();
+        if (name.equals("bin") || name.equals("src"))
+            return f.getParentFile().getName();
+        else {
+            if (name.endsWith(".jar"))
+                name = name.substring(0, name.length() - 4);
+            return name;
+        }
+    }
+
+    public Jar(String string, InputStream resourceAsStream) throws IOException {
+        this(string, resourceAsStream, 0);
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String toString() {
+        return "Jar:" + name;
+    }
+
+    public boolean putResource(String path, Resource resource) {
+        return putResource(path, resource, true);
+    }
+
+    public boolean putResource(String path, Resource resource, boolean overwrite) {
+        updateModified(resource.lastModified(), path);
+
+        if (path.equals("META-INF/MANIFEST.MF")) {
+            manifest = null;
+            if (resources.isEmpty())
+                manifestFirst = true;
+        }
+        String dir = getDirectory(path);
+        Map<String, Resource> s = directories.get(dir);
+        if (s == null) {
+            s = new TreeMap<String, Resource>();
+            directories.put(dir, s);
+            int n = dir.lastIndexOf('/');
+            while (n > 0) {
+                String dd = dir.substring(0, n);
+                if (directories.containsKey(dd))
+                    break;
+                directories.put(dd, null);
+                n = dd.lastIndexOf('/');
+            }
+        }
+        boolean duplicate = s.containsKey(path);
+        if (!duplicate || overwrite) {
+            resources.put(path, resource);
+            s.put(path, resource);
+        }
+        return duplicate;
+    }
+
+    public Resource getResource(String path) {
+        return resources.get(path);
+    }
+
+    private String getDirectory(String path) {
+        int n = path.lastIndexOf('/');
+        if (n < 0)
+            return "";
+
+        return path.substring(0, n);
+    }
+
+    public Map<String, Map<String, Resource>> getDirectories() {
+        return directories;
+    }
+
+    public Map<String, Resource> getResources() {
+        return resources;
+    }
+
+    public boolean addDirectory(Map<String, Resource> directory,
+            boolean overwrite) {
+        boolean duplicates = false;
+        if (directory == null)
+            return false;
+
+        for (Map.Entry<String, Resource> entry : directory.entrySet()) {
+            String key = entry.getKey();
+            if (!key.endsWith(".java")) {
+                duplicates |= putResource(key, (Resource) entry.getValue(),
+                        overwrite);
+            }
+        }
+        return duplicates;
+    }
+
+    public Manifest getManifest() throws IOException {
+        if (manifest == null) {
+            Resource manifestResource = getResource("META-INF/MANIFEST.MF");
+            if (manifestResource != null) {
+                InputStream in = manifestResource.openInputStream();
+                manifest = new Manifest(in);
+                in.close();
+            }
+        }
+        return manifest;
+    }
+
+    public boolean exists(String path) {
+        return resources.containsKey(path);
+    }
+
+    public void setManifest(Manifest manifest) {
+        manifestFirst = true;
+        this.manifest = manifest;
+    }
+
+    public void write(File file) throws Exception {
+        try {
+            OutputStream out = new FileOutputStream(file);
+            write(out);
+            out.close();
+            return;
+
+        } catch (Exception t) {
+            file.delete();
+            throw t;
+        }
+    }
+
+    public void write(String file) throws Exception {
+        write(new File(file));
+    }
+
+    public void write(OutputStream out) throws IOException {
+        ZipOutputStream jout = nomanifest ? new ZipOutputStream(out) : new JarOutputStream(out);
+        Set<String> done = new HashSet<String>();
+
+        Set<String> directories = new HashSet<String>();
+        if (doNotTouchManifest) {
+            writeResource(jout, directories, "META-INF/MANIFEST.MF",
+                    getResource("META-INF/MANIFEST.MF"));
+            done.add("META-INF/MANIFEST.MF");
+        } else if (!nomanifest)
+            doManifest(done, jout);
+
+        for (Map.Entry<String, Resource> entry : getResources().entrySet()) {
+            // Skip metainf contents
+            if (!done.contains(entry.getKey()))
+                writeResource(jout, directories, (String) entry.getKey(),
+                        (Resource) entry.getValue());
+        }
+        jout.finish();
+    }
+
+    private void doManifest(Set<String> done, ZipOutputStream jout)
+            throws IOException {
+        if ( nomanifest )
+            return;
+        
+        JarEntry ze = new JarEntry("META-INF/MANIFEST.MF");
+        jout.putNextEntry(ze);
+        writeManifest(jout);
+        jout.closeEntry();
+        done.add(ze.getName());
+    }
+
+    /**
+     * Cleanup the manifest for writing. Cleaning up consists of adding a space
+     * after any \n to prevent the manifest to see this newline as a delimiter.
+     * 
+     * @param out
+     *            Output
+     * @throws IOException
+     */
+
+    public void writeManifest(OutputStream out) throws IOException {
+        writeManifest(getManifest(), out);
+    }
+
+    public static void writeManifest(Manifest manifest, OutputStream out)
+            throws IOException {
+        
+        manifest = clean(manifest);
+        manifest.write(out);
+    }
+
+    private static Manifest clean(Manifest org) {
+
+        Manifest result = new Manifest();
+        for (Map.Entry<?, ?> entry : org.getMainAttributes().entrySet()) {
+            String nice = clean((String) entry.getValue());
+            result.getMainAttributes().put(entry.getKey(), nice);
+        }
+        for (String name : org.getEntries().keySet()) {
+            Attributes attrs = result.getAttributes(name);
+            if (attrs == null) {
+                attrs = new Attributes();
+                result.getEntries().put(name, attrs);
+            }
+
+            for (Map.Entry<?, ?> entry : org.getAttributes(name).entrySet()) {
+                String nice = clean((String) entry.getValue());
+                attrs.put((Attributes.Name) entry.getKey(), nice);
+            }
+        }
+        return result;
+    }
+
+    private static String clean(String s) {
+        if (s.indexOf('\n') < 0)
+            return s;
+
+        StringBuffer sb = new StringBuffer(s);
+        for (int i = 0; i < sb.length(); i++) {
+            if (sb.charAt(i) == '\n')
+                sb.insert(++i, ' ');
+        }
+        return sb.toString();
+    }
+
+    private void writeResource(ZipOutputStream jout, Set<String> directories,
+            String path, Resource resource) throws IOException {
+        if (resource == null)
+            return;
+
+        createDirectories(directories, jout, path);
+        ZipEntry ze = new ZipEntry(path);
+        ze.setMethod(ZipEntry.DEFLATED);
+        long lastModified = resource.lastModified();
+        if (lastModified == 0L) {
+            lastModified = System.currentTimeMillis();
+        }
+        ze.setTime(lastModified);
+        if (resource.getExtra() != null)
+            ze.setExtra(resource.getExtra().getBytes());
+        jout.putNextEntry(ze);
+        try {
+            resource.write(jout);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Cannot write resource: " + path
+                    + " " + resource + " exception: " + e);
+        }
+        jout.closeEntry();
+    }
+
+    void createDirectories(Set<String> directories, ZipOutputStream zip,
+            String name) throws IOException {
+        int index = name.lastIndexOf('/');
+        if (index > 0) {
+            String path = name.substring(0, index);
+            if (directories.contains(path))
+                return;
+            createDirectories(directories, zip, path);
+            ZipEntry ze = new ZipEntry(path + '/');
+            zip.putNextEntry(ze);
+            zip.closeEntry();
+            directories.add(path);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Add all the resources in the given jar that match the given filter.
+     * 
+     * @param sub
+     *            the jar
+     * @param filter
+     *            a pattern that should match the resoures in sub to be added
+     */
+    public boolean addAll(Jar sub, Pattern filter) {
+        boolean dupl = false;
+        for (String name : sub.getResources().keySet()) {
+            if ("META-INF/MANIFEST.MF".equals(name))
+                continue;
+
+            if (filter == null || filter.matcher(name).matches())
+                dupl |= putResource(name, sub.getResource(name), true);
+        }
+        return dupl;
+    }
+
+    public void close() {
+        if (zipFile != null)
+            try {
+                zipFile.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        resources = null;
+        directories = null;
+        manifest = null;
+        source = null;
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public void updateModified(long time, String reason) {
+        if (time > lastModified) {
+            lastModified = time;
+            lastModifiedReason = reason;
+        }
+    }
+
+    public void setReporter(Reporter reporter) {
+        this.reporter = reporter;
+    }
+
+    public boolean hasDirectory(String path) {
+        return directories.get(path) != null;
+    }
+
+    public List<String> getPackages() {
+        List<String> list = new ArrayList<String>(directories.size());
+
+        for (Iterator<String> i = directories.keySet().iterator(); i.hasNext();) {
+            String path = i.next();
+            String pack = path.replace('/', '.');
+            list.add(pack);
+        }
+        return list;
+    }
+
+    public File getSource() {
+        return source;
+    }
+
+    public boolean addAll(Jar src) {
+        return addAll(src, null);
+    }
+
+    public boolean rename(String oldPath, String newPath) {
+        Resource resource = remove(oldPath);
+        if (resource == null)
+            return false;
+
+        return putResource(newPath, resource);
+    }
+
+    public Resource remove(String path) {
+        Resource resource = resources.remove(path);
+        String dir = getDirectory(path);
+        Map<String, Resource> mdir = directories.get(dir);
+        // must be != null
+        mdir.remove(path);
+        return resource;
+    }
+
+    /**
+     * Make sure nobody touches the manifest! If the bundle is signed, we do not
+     * want anybody to touch the manifest after the digests have been
+     * calculated.
+     */
+    public void setDoNotTouchManifest() {
+        doNotTouchManifest = true;
+    }
+
+    public void setNoManifest(boolean b) {
+        nomanifest = b;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/JarResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/JarResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/JarResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,46 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public class JarResource implements Resource {
+	Jar		jar;
+	String extra;
+	
+	public JarResource(Jar jar ) {
+		this.jar = jar;
+	}
+	
+	public String getExtra() {
+		return extra;
+	}
+
+	public long lastModified() {
+		return jar.lastModified();
+	}
+
+
+	public void write(OutputStream out) throws IOException {
+		jar.write(out);
+	}
+	
+	public InputStream openInputStream() throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		write(out);
+		out.close();
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		return in;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+	
+	public Jar getJar() { 
+	    return jar;
+	}
+	
+	public String toString() {
+	    return ":" + jar.getName() + ":";
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Macro.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Macro.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Macro.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,840 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.libg.sed.*;
+import aQute.libg.version.*;
+
+/**
+ * Provide a macro processor. This processor 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 processor (without the _). Macros can nest to any
+ * depth but may not contain loops.
+ * 
+ */
+public class Macro implements Replacer {
+    Properties properties;
+    Processor  domain;
+    Object     targets[];
+    boolean    flattening;
+
+    public Macro(Properties properties, Processor domain, Object... targets) {
+        this.properties = properties;
+        this.domain = domain;
+        this.targets = targets;
+        if (targets != null) {
+            for (Object o : targets) {
+                assert o != null;
+            }
+        }
+    }
+
+    public Macro(Processor processor) {
+        this(new Properties(), processor);
+    }
+
+    public String process(String line) {
+        return process(line, null);
+    }
+
+    String process(String line, Link link) {
+        StringBuffer sb = new StringBuffer();
+        process(line, 0, '\u0000', '\u0000', sb, link);
+        return sb.toString();
+    }
+
+    int process(CharSequence org, int index, char begin, char end,
+            StringBuffer result, Link link) {
+        StringBuilder line = new StringBuilder(org);
+        int nesting = 1;
+
+        StringBuffer variable = new StringBuffer();
+        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;
+                }
+            }
+            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;
+    }
+
+    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) {
+                String value = (String) properties.getProperty(key);
+                if (value != null)
+                    return process(value, new Link(link, key));
+
+                value = doCommands(key);
+                if (value != null)
+                    return process(value, new Link(link, key));
+
+                if (key != null && key.trim().length() > 0) {
+                    value = System.getProperty(key);
+                    if (value != null)
+                        return value;
+                }
+                if (!flattening)
+                    domain.warning("No translation found for macro: " + key);
+            } else {
+                domain.warning("Found empty macro key");
+            }
+        } else {
+            domain.warning("Found null macro key");
+        }
+        return "${" + key + "}";
+    }
+
+    /**
+     * 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) {
+        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("\\\\;", ";");
+
+        Processor rover = domain;
+        while (rover != null) {
+            String result = doCommand(rover, args[0], args);
+            if (result != null)
+                return result;
+
+            rover = rover.getParent();
+        }
+
+        for (int i = 0; targets != null && i < targets.length; i++) {
+            String result = doCommand(targets[i], 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.out.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) {
+                domain.warning("Exception in replace: " + e.getCause());
+                e.printStackTrace();
+            } catch (Exception e) {
+                domain.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++) {
+            Processor.split(args[i], set);
+        }
+        return Processor.join(set, ",");
+    }
+
+    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);
+
+        Collection<String> list = new ArrayList<String>(Processor
+                .split(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 Processor.join(list);
+    }
+
+    static String _sortHelp = "${sort;<list>...}";
+
+    public String _sort(String args[]) {
+        verifyCommand(args, _sortHelp, null, 2, Integer.MAX_VALUE);
+
+        List<String> result = new ArrayList<String>();
+        for (int i = 1; i < args.length; i++) {
+            Processor.split(args[i], result);
+        }
+        Collections.sort(result);
+        return Processor.join(result);
+    }
+
+    static String _joinHelp = "${join;<list>...}";
+
+    public String _join(String args[]) {
+
+        verifyCommand(args, _joinHelp, null, 1, Integer.MAX_VALUE);
+
+        List<String> result = new ArrayList<String>();
+        for (int i = 1; i < args.length; i++) {
+            Processor.split(args[i], result);
+        }
+        return Processor.join(result);
+    }
+
+    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.length() != 0)
+            return args[2];
+        if (args.length > 3)
+            return args[3];
+        else
+            return "";
+    }
+
+    public String _now(String args[]) {
+        return new Date().toString();
+    }
+
+    public 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 ArrayList<String>();
+        for (int i = 1; i < args.length; i++) {
+            Processor.split(args[i], names);
+        }
+        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 = properties.getProperty(args[1]);
+        if (value == null)
+            return "";
+        else
+            return value;
+    }
+
+    /**
+     * 
+     * replace ; <list> ; regex ; replace
+     * 
+     * @param args
+     * @return
+     */
+    public String _replace(String args[]) {
+        if (args.length != 4) {
+            domain.warning("Invalid nr of arguments to replace "
+                    + Arrays.asList(args));
+            return null;
+        }
+
+        String list[] = args[1].split("\\s*,\\s*");
+        StringBuffer sb = new StringBuffer();
+        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++) {
+            domain.warning(process(args[i]));
+        }
+        return "";
+    }
+
+    public String _error(String args[]) {
+        for (int i = 1; i < args.length; i++) {
+            domain.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 = Processor.split(args[1]);
+
+        List<String> names = new ArrayList<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 {
+                domain
+                        .warning("in toclassname, "
+                                + args[1]
+                                + " is not a class path because it does not end in .class");
+            }
+        }
+        return Processor.join(names, ",");
+    }
+
+    /**
+     * 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 = new Boolean(args[2]);
+        
+        Collection<String> names = Processor.split(args[1]);
+        Collection<String> paths = new ArrayList<String>(names.size());
+        for (String name : names) {
+            String path = name.replace('.', '/') + (cl ? ".class" : "");
+            paths.add(path);
+        }
+        return Processor.join(paths, ",");
+    }
+
+    public String _dir(String args[]) {
+        if (args.length < 2) {
+            domain.warning("Need at least one file name for ${dir;...}");
+            return null;
+        } else {
+            String del = "";
+            StringBuffer sb = new StringBuffer();
+            for (int i = 1; i < args.length; i++) {
+                File f = new File(args[i]).getAbsoluteFile();
+                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) {
+            domain.warning("Need at least one file name for ${basename;...}");
+            return null;
+        } else {
+            String del = "";
+            StringBuffer sb = new StringBuffer();
+            for (int i = 1; i < args.length; i++) {
+                File f = new File(args[i]).getAbsoluteFile();
+                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) {
+            domain.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) {
+            domain.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();
+
+        if (args.length > 1) {
+            format = args[1];
+            if (args.length > 2) {
+                now = Long.parseLong(args[2]);
+                if (args.length > 3) {
+                    domain.warning("Too many arguments for tstamp: "
+                            + Arrays.toString(args));
+                }
+            }
+        }
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        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 = new File(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);
+
+        String[] files = dir.list();
+        List<String> result;
+
+        if (args.length < 3) {
+            result = Arrays.asList(files);
+        } else
+            result = new ArrayList<String>();
+
+        for (int i = 2; i < args.length; i++) {
+            String parts[] = args[i].split("\\s*,\\s*");
+            for (String pattern : parts) {
+                // So make it in to an instruction
+                Instruction instr = Instruction.getPattern(pattern);
+
+                // For each project, match it against the instruction
+                for (int f = 0; f < files.length; f++) {
+                    if (files[f] != null) {
+                        if (instr.matches(files[f])) {
+                            if (!instr.isNegated()) {
+                                if (relative)
+                                    result.add(files[f]);
+                                else
+                                    result.add(new File(dir, files[f])
+                                            .getAbsolutePath());
+                            }
+                            files[f] = null;
+                        }
+                    }
+                }
+            }
+        }
+        return Processor.join(result, ",");
+    }
+
+    public String _currenttime(String args[]) {
+        return Long.toString(System.currentTimeMillis());
+    }
+
+    /**
+     * Modify a version to set a version policy. Thed policy is a mask that is
+     * mapped to a version.
+     * 
+     * <pre>
+     * +           increment
+     * -           decrement
+     * =           maintain
+     * &tilde;           discard
+     * 
+     * ==+      = maintain major, minor, increment micro, discard qualifier
+     * &tilde;&tilde;&tilde;=     = just get the qualifier
+     * version=&quot;[${version;==;${@}},${version;=+;${@}})&quot;
+     * </pre>
+     * 
+     * 
+     * 
+     * 
+     * @param args
+     * @return
+     */
+    static String  _versionHelp      = "${version;<mask>;<version>}, modify a version\n"
+                                             + "<mask> ::= [ M [ M [ M [ MQ ]]]\n"
+                                             + "M ::= '+' | '-' | MQ\n"
+                                             + "MQ ::= '~' | '='";
+    static Pattern _versionPattern[] = new Pattern[] { null, null,
+            Pattern.compile("[-+=~]{0,3}[=~]?"), Verifier.VERSION };
+
+    public String _version(String args[]) {
+        verifyCommand(args, _versionHelp, null, 3, 3);
+
+        String mask = args[1];
+
+        Version version = new Version(args[2]);
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+
+        for (int i = 0; i < mask.length(); i++) {
+            char c = mask.charAt(i);
+            String result = null;
+            if (c != '~') {
+                if (i == 3) {
+                    result = version.getQualifier();
+                } else if (Character.isDigit(c)) {
+                	// Handle masks like +00, =+0
+                	result = String.valueOf(c);
+                } else {
+                    int x = version.get(i);
+                    switch (c) {
+                    case '+':
+                        x++;
+                        break;
+                    case '-':
+                        x--;
+                        break;
+                    case '=':
+                        break;
+                    }
+                    result = Integer.toString(x);
+                }
+                if (result != null) {
+                    sb.append(del);
+                    del = ".";
+                    sb.append(result);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * System command. Execute a command and insert the result.
+     * 
+     * @param args
+     * @param help
+     * @param patterns
+     * @param low
+     * @param high
+     */
+    public String _system(String args[]) throws Exception {
+        verifyCommand(args,
+                "${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,
+                domain.getBase());
+        if (input != null) {
+            process.getOutputStream().write(input.getBytes("UTF-8"));
+        }
+        process.getOutputStream().close();
+
+        String s = getString(process.getInputStream());
+        process.getInputStream().close();
+        int exitValue = process.waitFor();
+        if (exitValue != 0) {
+            domain.error("System command " + command + " failed with "
+                    + exitValue);
+        }
+        return s.trim();
+    }
+
+    /**
+     * 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 = domain.getFile(args[1]);
+        if (f.isFile()) {
+            InputStream in = new FileInputStream(f);
+            return getString(in);
+        } else if (f.isDirectory()) {
+            return Arrays.toString(f.list());
+        } else {
+            try {
+                URL url = new URL(args[1]);
+                InputStream in = url.openStream();
+                return getString(in);
+            } catch (MalformedURLException mfue) {
+                // Ignore here
+            }
+            return null;
+        }
+    }
+
+    public static String getString(InputStream in) throws IOException {
+        try {
+            StringBuilder sb = new StringBuilder();
+            BufferedReader rdr = new BufferedReader(new InputStreamReader(in));
+            String line = null;
+            while ((line = rdr.readLine()) != null) {
+                sb.append(line);
+                sb.append("\n");
+            }
+            return sb.toString();
+        } finally {
+            in.close();
+        }
+    }
+
+    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 - 1; i++) {
+                if (patterns[i] != null
+                        && !patterns[i].matcher(args[i + 1]).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;
+
+        public Link(Link previous, String key) {
+            this.previous = previous;
+            this.key = key;
+        }
+
+        public boolean contains(String key) {
+            if (this.key.equals(key))
+                return true;
+
+            if (previous == null)
+                return false;
+
+            return previous.contains(key);
+        }
+
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            String del = "[";
+            for (Link r = this; r != null; r = r.previous) {
+                sb.append(del);
+                sb.append(r.key);
+                del = ",";
+            }
+            sb.append("]");
+            return sb.toString();
+        }
+    }
+
+    /**
+     * 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 Properties getFlattenedProperties() {
+        // Some macros only work in a lower processor, so we
+        // do not report unknown macros while flattening
+        flattening = true;
+        try {
+            Properties flattened = new Properties();
+            for (Enumeration<?> e = properties.propertyNames(); e
+                    .hasMoreElements();) {
+                String key = (String) e.nextElement();
+                if (!key.startsWith("_"))
+                    if ( key.startsWith("-"))
+                        flattened.put(key, properties.getProperty(key));
+                    else
+                        flattened.put(key, process(properties.getProperty(key)));
+            }
+            return flattened;
+        } finally {
+            flattening = false;
+        }
+    };
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/OpCodes.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/OpCodes.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/OpCodes.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1196 @@
+package aQute.lib.osgi;
+
+public class OpCodes {
+	final static short	nop				= 0x00;			// [No change] performs
+														// no
+	// operation
+	final static short	aconst_null		= 0x01;			// ? null pushes a null
+	// reference onto the stack
+	final static short	iconst_m1		= 0x02;			// ? -1 loads the int
+														// value -1
+	// onto the stack
+	final static short	iconst_0		= 0x03;			// ? 0 loads the int
+														// value 0
+	// onto the stack
+	final static short	iconst_1		= 0x04;			// ? 1 loads the int
+														// value 1
+	// onto the stack
+	final static short	iconst_2		= 0x05;			// ? 2 loads the int
+														// value 2
+	// onto the stack
+	final static short	iconst_3		= 0x06;			// ? 3 loads the int
+														// value 3
+	// onto the stack
+	final static short	iconst_4		= 0x07;			// ? 4 loads the int
+														// value 4
+	// onto the stack
+	final static short	iconst_5		= 0x08;			// ? 5 loads the int
+														// value 5
+	// onto the stack
+	final static short	lconst_0		= 0x09;			// ? 0L pushes the long
+														// 0 onto
+	// the stack
+	final static short	bipush			= 0x10;			// byte ? value pushes a
+														// byte
+	// onto the stack as an integer
+	// value
+	final static short	sipush			= 0x11;			// byte1, byte2 ? value
+														// pushes a
+	// signed integer (byte1 << 8 +
+	// byte2) onto the stack
+	final static short	ldc				= 0x12;			// index ? value pushes
+														// a
+	// constant #index from a
+	// constant pool (String, int,
+	// float or class type) onto the
+	// stack
+	final static short	ldc_w			= 0x13;			// indexbyte1,
+														// indexbyte2 ?
+	// value pushes a constant
+	// #index from a constant pool
+	// (String, int, float or class
+	// type) onto the stack (wide
+	// index is constructed as
+	// indexbyte1 << 8 + indexbyte2)
+	final static short	ldc2_w			= 0x14;			// indexbyte1,
+														// indexbyte2 ?
+	// value pushes a constant
+	// #index from a constant pool
+	// (double or long) onto the
+	// stack (wide index is
+	// constructed as indexbyte1 <<
+	// 8 + indexbyte2)
+	final static short	iload			= 0x15;			// index ? value loads
+														// an int
+	// value from a variable #index
+	final static short	lload			= 0x16;			// index ? value load a
+														// long
+	// value from a local variable
+	// #index
+	final static short	fload			= 0x17;			// index ? value loads a
+														// float
+	// value from a local variable
+	// #index
+	final static short	dload			= 0x18;			// index ? value loads a
+														// double
+	// value from a local variable
+	// #index
+	final static short	aload			= 0x19;			// index ? objectref
+														// loads a
+	// reference onto the stack from
+	// a local variable #index
+	final static short	lload_2			= 0x20;			// ? value load a long
+														// value
+	// from a local variable 2
+	final static short	lload_3			= 0x21;			// ? value load a long
+														// value
+	// from a local variable 3
+	final static short	fload_0			= 0x22;			// ? value loads a float
+														// value
+	// from local variable 0
+	final static short	fload_1			= 0x23;			// ? value loads a float
+														// value
+	// from local variable 1
+	final static short	fload_2			= 0x24;			// ? value loads a float
+														// value
+	// from local variable 2
+	final static short	fload_3			= 0x25;			// ? value loads a float
+														// value
+	// from local variable 3
+	final static short	dload_0			= 0x26;			// ? value loads a
+														// double from
+	// local variable 0
+	final static short	dload_1			= 0x27;			// ? value loads a
+														// double from
+	// local variable 1
+	final static short	dload_2			= 0x28;			// ? value loads a
+														// double from
+	// local variable 2
+	final static short	dload_3			= 0x29;			// ? value loads a
+														// double from
+	// local variable 3
+	final static short	faload			= 0x30;			// arrayref, index ?
+														// value loads
+	// a float from an array
+	final static short	daload			= 0x31;			// arrayref, index ?
+														// value loads
+	// a double from an array
+	final static short	aaload			= 0x32;			// arrayref, index ?
+														// value loads
+	// onto the stack a reference
+	// from an array
+	final static short	baload			= 0x33;			// arrayref, index ?
+														// value loads
+	// a byte or Boolean value from
+	// an array
+	final static short	caload			= 0x34;			// arrayref, index ?
+														// value loads
+	// a char from an array
+	final static short	saload			= 0x35;			// arrayref, index ?
+														// value load
+	// short from array
+	final static short	istore			= 0x36;			// index value ? store
+														// int value
+	// into variable #index
+	final static short	lstore			= 0x37;			// index value ? store a
+														// long
+	// value in a local variable
+	// #index
+	final static short	fstore			= 0x38;			// index value ? stores
+														// a float
+	// value into a local variable
+	// #index
+	final static short	dstore			= 0x39;			// index value ? stores
+														// a double
+	// value into a local variable
+	// #index
+	final static short	lstore_1		= 0x40;			// value ? store a long
+														// value in
+	// a local variable 1
+	final static short	lstore_2		= 0x41;			// value ? store a long
+														// value in
+	// a local variable 2
+	final static short	lstore_3		= 0x42;			// value ? store a long
+														// value in
+	// a local variable 3
+	final static short	fstore_0		= 0x43;			// value ? stores a
+														// float value
+	// into local variable 0
+	final static short	fstore_1		= 0x44;			// value ? stores a
+														// float value
+	// into local variable 1
+	final static short	fstore_2		= 0x45;			// value ? stores a
+														// float value
+	// into local variable 2
+	final static short	fstore_3		= 0x46;			// value ? stores a
+														// float value
+	// into local variable 3
+	final static short	dstore_0		= 0x47;			// value ? stores a
+														// double into
+	// local variable 0
+	final static short	dstore_1		= 0x48;			// value ? stores a
+														// double into
+	// local variable 1
+	final static short	dstore_2		= 0x49;			// value ? stores a
+														// double into
+	// local variable 2
+	final static short	lastore			= 0x50;			// arrayref, index,
+														// value ?
+	// store a long to an array
+	final static short	fastore			= 0x51;			// arreyref, index,
+														// value ?
+	// stores a float in an array
+	final static short	dastore			= 0x52;			// arrayref, index,
+														// value ?
+	// stores a double into an array
+	final static short	aastore			= 0x53;			// arrayref, index,
+														// value ?
+	// stores into a reference to an
+	// array
+	final static short	bastore			= 0x54;			// arrayref, index,
+														// value ?
+	// stores a byte or Boolean
+	// value into an array
+	final static short	castore			= 0x55;			// arrayref, index,
+														// value ?
+	// stores a char into an array
+	final static short	sastore			= 0x56;			// arrayref, index,
+														// value ?
+	// store short to array
+	final static short	pop				= 0x57;			// value ? discards the
+														// top
+	// value on the stack
+	final static short	pop2			= 0x58;			// {value2, value1} ?
+														// discards
+	// the top two values on the
+	// stack (or one value, if it is
+	// a double or long)
+	final static short	dup				= 0x59;			// value ? value, value
+	// duplicates the value on top
+	// of the stack
+	final static short	iadd			= 0x60;			// value1, value2 ?
+														// result adds
+	// two ints together
+	final static short	ladd			= 0x61;			// value1, value2 ?
+														// result add
+	// two longs
+	final static short	fadd			= 0x62;			// value1, value2 ?
+														// result adds
+	// two floats
+	final static short	dadd			= 0x63;			// value1, value2 ?
+														// result adds
+	// two doubles
+	final static short	isub			= 0x64;			// value1, value2 ?
+														// result int
+	// subtract
+	final static short	lsub			= 0x65;			// value1, value2 ?
+														// result
+	// subtract two longs
+	final static short	fsub			= 0x66;			// value1, value2 ?
+														// result
+	// subtracts two floats
+	final static short	dsub			= 0x67;			// value1, value2 ?
+														// result
+	// subtracts a double from
+	// another
+	final static short	imul			= 0x68;			// value1, value2 ?
+														// result
+	// multiply two integers
+	final static short	lmul			= 0x69;			// value1, value2 ?
+														// result
+	// multiplies two longs
+	final static short	irem			= 0x70;			// value1, value2 ?
+														// result
+	// logical int remainder
+	final static short	lrem			= 0x71;			// value1, value2 ?
+														// result
+	// remainder of division of two
+	// longs
+	final static short	frem			= 0x72;			// value1, value2 ?
+														// result gets
+	// the remainder from a division
+	// between two floats
+	final static short	drem			= 0x73;			// value1, value2 ?
+														// result gets
+	// the remainder from a division
+	// between two doubles
+	final static short	ineg			= 0x74;			// value ? result negate
+														// int
+	final static short	lneg			= 0x75;			// value ? result
+														// negates a long
+	final static short	fneg			= 0x76;			// value ? result
+														// negates a
+	// float
+	final static short	dneg			= 0x77;			// value ? result
+														// negates a
+	// double
+	final static short	ishl			= 0x78;			// value1, value2 ?
+														// result int
+	// shift left
+	final static short	lshl			= 0x79;			// value1, value2 ?
+														// result
+	// bitwise shift left of a long
+	// value1 by value2 positions
+	final static short	ior				= 0x80;			// value1, value2 ?
+														// result
+	// logical int or
+	final static short	lor				= 0x81;			// value1, value2 ?
+														// result
+	// bitwise or of two longs
+	final static short	ixor			= 0x82;			// value1, value2 ?
+														// result int
+	// xor
+	final static short	lxor			= 0x83;			// value1, value2 ?
+														// result
+	// bitwise exclusive or of two
+	// longs
+	final static short	iinc			= 0x84;			// index, const [No
+														// change]
+	// increment local variable
+	// #index by signed byte const
+	final static short	i2l				= 0x85;			// value ? result
+														// converts an
+	// int into a long
+	final static short	i2f				= 0x86;			// value ? result
+														// converts an
+	// int into a float
+	final static short	i2d				= 0x87;			// value ? result
+														// converts an
+	// int into a double
+	final static short	l2i				= 0x88;			// value ? result
+														// converts a
+	// long to an int
+	final static short	l2f				= 0x89;			// value ? result
+														// converts a
+	// long to a float
+	final static short	d2f				= 0x90;			// value ? result
+														// converts a
+	// double to a float
+	final static short	i2b				= 0x91;			// value ? result
+														// converts an
+	// int into a byte
+	final static short	i2c				= 0x92;			// value ? result
+														// converts an
+	// int into a character
+	final static short	i2s				= 0x93;			// value ? result
+														// converts an
+	// int into a short
+	final static short	lcmp			= 0x94;			// value1, value2 ?
+														// result
+	// compares two longs values
+	final static short	fcmpl			= 0x95;			// value1, value2 ?
+														// result
+	// compares two floats
+	final static short	fcmpg			= 0x96;			// value1, value2 ?
+														// result
+	// compares two floats
+	final static short	dcmpl			= 0x97;			// value1, value2 ?
+														// result
+	// compares two doubles
+	final static short	dcmpg			= 0x98;			// value1, value2 ?
+														// result
+	// compares two doubles
+	final static short	ifeq			= 0x99;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is 0, branch
+	// to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	lconst_1		= 0x0a;			// ? 1L pushes the long
+														// 1 onto
+	// the stack
+	final static short	fconst_0		= 0x0b;			// ? 0.0f pushes 0.0f on
+														// the
+	// stack
+	final static short	fconst_1		= 0x0c;			// ? 1.0f pushes 1.0f on
+														// the
+	// stack
+	final static short	fconst_2		= 0x0d;			// ? 2.0f pushes 2.0f on
+														// the
+	// stack
+	final static short	dconst_0		= 0x0e;			// ? 0.0 pushes the
+														// constant 0.0
+	// onto the stack
+	final static short	dconst_1		= 0x0f;			// ? 1.0 pushes the
+														// constant 1.0
+	// onto the stack
+	final static short	iload_0			= 0x1a;			// ? value loads an int
+														// value
+	// from variable 0
+	final static short	iload_1			= 0x1b;			// ? value loads an int
+														// value
+	// from variable 1
+	final static short	iload_2			= 0x1c;			// ? value loads an int
+														// value
+	// from variable 2
+	final static short	iload_3			= 0x1d;			// ? value loads an int
+														// value
+	// from variable 3
+	final static short	lload_0			= 0x1e;			// ? value load a long
+														// value
+	// from a local variable 0
+	final static short	lload_1			= 0x1f;			// ? value load a long
+														// value
+	// from a local variable 1
+	final static short	aload_0			= 0x2a;			// ? objectref loads a
+														// reference
+	// onto the stack from local
+	// variable 0
+	final static short	aload_1			= 0x2b;			// ? objectref loads a
+														// reference
+	// onto the stack from local
+	// variable 1
+	final static short	aload_2			= 0x2c;			// ? objectref loads a
+														// reference
+	// onto the stack from local
+	// variable 2
+	final static short	aload_3			= 0x2d;			// ? objectref loads a
+														// reference
+	// onto the stack from local
+	// variable 3
+	final static short	iaload			= 0x2e;			// arrayref, index ?
+														// value loads
+	// an int from an array
+	final static short	laload			= 0x2f;			// arrayref, index ?
+														// value load
+	// a long from an array
+	final static short	astore			= 0x3a;			// index objectref ?
+														// stores a
+	// reference into a local
+	// variable #index
+	final static short	istore_0		= 0x3b;			// value ? store int
+														// value into
+	// variable 0
+	final static short	istore_1		= 0x3c;			// value ? store int
+														// value into
+	// variable 1
+	final static short	istore_2		= 0x3d;			// value ? store int
+														// value into
+	// variable 2
+	final static short	istore_3		= 0x3e;			// value ? store int
+														// value into
+	// variable 3
+	final static short	lstore_0		= 0x3f;			// value ? store a long
+														// value in
+	// a local variable 0
+	final static short	dstore_3		= 0x4a;			// value ? stores a
+														// double into
+	// local variable 3
+	final static short	astore_0		= 0x4b;			// objectref ? stores a
+	// reference into local variable
+	// 0
+	final static short	astore_1		= 0x4c;			// objectref ? stores a
+	// reference into local variable
+	// 1
+	final static short	astore_2		= 0x4d;			// objectref ? stores a
+	// reference into local variable
+	// 2
+	final static short	astore_3		= 0x4e;			// objectref ? stores a
+	// reference into local variable
+	// 3
+	final static short	iastore			= 0x4f;			// arrayref, index,
+														// value ?
+	// stores an int into an array
+	final static short	dup_x1			= 0x5a;			// value2, value1 ?
+														// value1,
+	// value2, value1 inserts a copy
+	// of the top value into the
+	// stack two values from the top
+	final static short	dup_x2			= 0x5b;			// value3, value2,
+														// value1 ?
+	// value1, value3, value2,
+	// value1 inserts a copy of the
+	// top value into the stack two
+	// (if value2 is double or long
+	// it takes up the entry of
+	// value3, too) or three values
+	// (if value2 is neither double
+	// nor long) from the top
+	final static short	dup2			= 0x5c;			// {value2, value1} ?
+														// {value2,
+	// value1}, {value2, value1}
+	// duplicate top two stack words
+	// (two values, if value1 is not
+	// double nor long; a single
+	// value, if value1 is double or
+	// long)
+	final static short	dup2_x1			= 0x5d;			// value3, {value2,
+														// value1} ?
+	// {value2, value1}, value3,
+	// {value2, value1} duplicate
+	// two words and insert beneath
+	// third word (see explanation
+	// above)
+	final static short	dup2_x2			= 0x5e;			// {value4, value3},
+														// {value2,
+	// value1} ? {value2, value1},
+	// {value4, value3}, {value2,
+	// value1} duplicate two words
+	// and insert beneath fourth
+	// word
+	final static short	swap			= 0x5f;			// value2, value1 ?
+														// value1,
+	// value2 swaps two top words on
+	// the stack (note that value1
+	// and value2 must not be double
+	// or long)
+	final static short	fmul			= 0x6a;			// value1, value2 ?
+														// result
+	// multiplies two floats
+	final static short	dmul			= 0x6b;			// value1, value2 ?
+														// result
+	// multiplies two doubles
+	final static short	idiv			= 0x6c;			// value1, value2 ?
+														// result
+	// divides two integers
+	final static short	ldiv			= 0x6d;			// value1, value2 ?
+														// result
+	// divide two longs
+	final static short	fdiv			= 0x6e;			// value1, value2 ?
+														// result
+	// divides two floats
+	final static short	ddiv			= 0x6f;			// value1, value2 ?
+														// result
+	// divides two doubles
+	final static short	ishr			= 0x7a;			// value1, value2 ?
+														// result int
+	// shift right
+	final static short	lshr			= 0x7b;			// value1, value2 ?
+														// result
+	// bitwise shift right of a long
+	// value1 by value2 positions
+	final static short	iushr			= 0x7c;			// value1, value2 ?
+														// result int
+	// shift right
+	final static short	lushr			= 0x7d;			// value1, value2 ?
+														// result
+	// bitwise shift right of a long
+	// value1 by value2 positions,
+	// unsigned
+	final static short	iand			= 0x7e;			// value1, value2 ?
+														// result
+	// performs a logical and on two
+	// integers
+	final static short	land			= 0x7f;			// value1, value2 ?
+														// result
+	// bitwise and of two longs
+	final static short	l2d				= 0x8a;			// value ? result
+														// converts a
+	// long to a double
+	final static short	f2i				= 0x8b;			// value ? result
+														// converts a
+	// float to an int
+	final static short	f2l				= 0x8c;			// value ? result
+														// converts a
+	// float to a long
+	final static short	f2d				= 0x8d;			// value ? result
+														// converts a
+	// float to a double
+	final static short	d2i				= 0x8e;			// value ? result
+														// converts a
+	// double to an int
+	final static short	d2l				= 0x8f;			// value ? result
+														// converts a
+	// double to a long
+	final static short	ifne			= 0x9a;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is not 0,
+	// branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	iflt			= 0x9b;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is less than
+	// 0, branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	ifge			= 0x9c;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is greater
+	// than or equal to 0, branch to
+	// instruction at branchoffset
+	// (signed short constructed
+	// from unsigned bytes
+	// branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	ifgt			= 0x9d;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is greater
+	// than 0, branch to instruction
+	// at branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	ifle			= 0x9e;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is less than
+	// or equal to 0, branch to
+	// instruction at branchoffset
+	// (signed short constructed
+	// from unsigned bytes
+	// branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmpeq		= 0x9f;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if ints are
+	// equal, branch to instruction
+	// at branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmpne		= 0xa0;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if ints are
+	// not equal, branch to
+	// instruction at branchoffset
+	// (signed short constructed
+	// from unsigned bytes
+	// branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmplt		= 0xa1;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if value1 is
+	// less than value2, branch to
+	// instruction at branchoffset
+	// (signed short constructed
+	// from unsigned bytes
+	// branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmpge		= 0xa2;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if value1 is
+	// greater than or equal to
+	// value2, branch to instruction
+	// at branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmpgt		= 0xa3;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if value1 is
+	// greater than value2, branch
+	// to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_icmple		= 0xa4;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if value1 is
+	// less than or equal to value2,
+	// branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_acmpeq		= 0xa5;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if
+	// references are equal, branch
+	// to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	if_acmpne		= 0xa6;			// branchbyte1,
+														// branchbyte2
+	// value1, value2 ? if
+	// references are not equal,
+	// branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	goto_			= 0xa7;			// branchbyte1,
+														// branchbyte2 [no
+	// change] goes to another
+	// instruction at branchoffset
+	// (signed short constructed
+	// from unsigned bytes
+	// branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	jsr				= 0xa8;			// branchbyte1,
+														// branchbyte2 ?
+	// address jump to subroutine at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2) and place the
+	// return address on the stack
+	final static short	ret				= 0xa9;			// index [No change]
+														// continue
+	// execution from address taken
+	// from a local variable #index
+	// (the asymmetry with jsr is
+	// intentional)
+	final static short	tableswitch		= 0xaa;			// [0-3 bytes padding],
+	// defaultbyte1, defaultbyte2,
+	// defaultbyte3, defaultbyte4,
+	// lowbyte1, lowbyte2, lowbyte3,
+	// lowbyte4, highbyte1,
+	// highbyte2, highbyte3,
+	// highbyte4, jump offsets...
+	// index ? continue execution
+	// from an address in the table
+	// at offset index
+	final static short	lookupswitch	= 0xab;			// <0-3 bytes padding>,
+	// defaultbyte1, defaultbyte2,
+	// defaultbyte3, defaultbyte4,
+	// npairs1, npairs2, npairs3,
+	// npairs4, match-offset
+	// pairs... key ? a target
+	// address is looked up from a
+	// table using a key and
+	// execution continues from the
+	// instruction at that address
+	final static short	ireturn			= 0xac;			// value ? [empty]
+														// returns an
+	// integer from a method
+	final static short	lreturn			= 0xad;			// value ? [empty]
+														// returns a
+	// long value
+	final static short	freturn			= 0xae;			// value ? [empty]
+														// returns a
+	// float
+	final static short	dreturn			= 0xaf;			// value ? [empty]
+														// returns a
+	// double from a method
+	final static short	areturn			= 0xb0;			// objectref ? [empty]
+														// returns a
+	// reference from a method
+	final static short	return_			= 0xb1;			// ? [empty] return void
+														// from
+	// method
+	final static short	getstatic		= 0xb2;			// index1, index2 ?
+														// value gets a
+	// static field value of a
+	// class, where the field is
+	// identified by field reference
+	// in the constant pool index
+	// (index1 << 8 + index2)
+	final static short	putstatic		= 0xb3;			// indexbyte1,
+														// indexbyte2 value
+	// ? set static field to value
+	// in a class, where the field
+	// is identified by a field
+	// reference index in constant
+	// pool (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	getfield		= 0xb4;			// index1, index2
+														// objectref ?
+	// value gets a field value of
+	// an object objectref, where
+	// the field is identified by
+	// field reference in the
+	// constant pool index (index1
+	// << 8 + index2)
+	final static short	putfield		= 0xb5;			// indexbyte1,
+														// indexbyte2
+	// objectref, value ? set field
+	// to value in an object
+	// objectref, where the field is
+	// identified by a field
+	// reference index in constant
+	// pool (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	invokevirtual	= 0xb6;			// indexbyte1,
+														// indexbyte2
+	// objectref, [arg1, arg2, ...]
+	// ? invoke virtual method on
+	// object objectref, where the
+	// method is identified by
+	// method reference index in
+	// constant pool (indexbyte1 <<
+	// 8 + indexbyte2)
+	final static short	invokespecial	= 0xb7;			// indexbyte1,
+														// indexbyte2
+	// objectref, [arg1, arg2, ...]
+	// ? invoke instance method on
+	// object objectref, where the
+	// method is identified by
+	// method reference index in
+	// constant pool (indexbyte1 <<
+	// 8 + indexbyte2)
+	final static short	invokestatic	= 0xb8;			// indexbyte1,
+														// indexbyte2 [arg1,
+	// arg2, ...] ? invoke a static
+	// method, where the method is
+	// identified by method
+	// reference index in constant
+	// pool (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	invokeinterface	= 0xb9;			// indexbyte1,
+														// indexbyte2,
+	// count, 0 objectref, [arg1,
+	// arg2, ...] ? invokes an
+	// interface method on object
+	// objectref, where the
+	// interface method is
+	// identified by method
+	// reference index in constant
+	// pool (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	xxxunusedxxx	= 0xba;			// this opcode is
+														// reserved "for
+	// historical reasons"
+	final static short	new_			= 0xbb;			// indexbyte1,
+														// indexbyte2 ?
+	// objectref creates new object
+	// of type identified by class
+	// reference in constant pool
+	// index (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	newarray		= 0xbc;			// atype count ?
+														// arrayref
+	// creates new array with count
+	// elements of primitive type
+	// identified by atype
+	final static short	anewarray		= 0xbd;			// indexbyte1,
+														// indexbyte2 count
+	// ? arrayref creates a new
+	// array of references of length
+	// count and component type
+	// identified by the class
+	// reference index (indexbyte1
+	// << 8 + indexbyte2) in the
+	// constant pool
+	final static short	arraylength		= 0xbe;			// arrayref ? length
+														// gets the
+	// length of an array
+	final static short	athrow			= 0xbf;			// objectref ? [empty],
+	// objectref throws an error or
+	// exception (notice that the
+	// rest of the stack is cleared,
+	// leaving only a reference to
+	// the Throwable)
+	final static short	checkcast		= 0xc0;			// indexbyte1,
+														// indexbyte2
+	// objectref ? objectref checks
+	// whether an objectref is of a
+	// certain type, the class
+	// reference of which is in the
+	// constant pool at index
+	// (indexbyte1 << 8 +
+	// indexbyte2)
+	final static short	instanceof_		= 0xc1;			// indexbyte1,
+														// indexbyte2
+	// objectref ? result determines
+	// if an object objectref is of
+	// a given type, identified by
+	// class reference index in
+	// constant pool (indexbyte1 <<
+	// 8 + indexbyte2)
+	final static short	monitorenter	= 0xc2;			// objectref ? enter
+														// monitor for
+	// object ("grab the lock" -
+	// start of synchronized()
+	// section)
+	final static short	monitorexit		= 0xc3;			// objectref ? exit
+														// monitor for
+	// object ("release the lock" -
+	// end of synchronized()
+	// section)
+	final static short	wide			= 0xc4;			// opcode, indexbyte1,
+	// indexbyte2
+	final static short	multianewarray	= 0xc5;			// indexbyte1,
+														// indexbyte2,
+	// dimensions count1,
+	// [count2,...] ? arrayref
+	// create a new array of
+	// dimensions dimensions with
+	// elements of type identified
+	// by class reference in
+	// constant pool index
+	// (indexbyte1 << 8 +
+	// indexbyte2); the sizes of
+	// each dimension is identified
+	// by count1, [count2, etc]
+	final static short	ifnull			= 0xc6;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is null,
+	// branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	ifnonnull		= 0xc7;			// branchbyte1,
+														// branchbyte2
+	// value ? if value is not null,
+	// branch to instruction at
+	// branchoffset (signed short
+	// constructed from unsigned
+	// bytes branchbyte1 << 8 +
+	// branchbyte2)
+	final static short	goto_w			= 0xc8;			// branchbyte1,
+														// branchbyte2,
+	// branchbyte3, branchbyte4 [no
+	// change] goes to another
+	// instruction at branchoffset
+	// (signed int constructed from
+	// unsigned bytes branchbyte1 <<
+	// 24 + branchbyte2 << 16 +
+	// branchbyte3 << 8 +
+	// branchbyte4)
+	final static short	jsr_w			= 0xc9;			// branchbyte1,
+														// branchbyte2,
+	// branchbyte3, branchbyte4 ?
+	// address jump to subroutine at
+	// branchoffset (signed int
+	// constructed from unsigned
+	// bytes branchbyte1 << 24 +
+	// branchbyte2 << 16 +
+	// branchbyte3 << 8 +
+	// branchbyte4) and place the
+	// return address on the stack
+	final static short	breakpoint		= 0xca;			// reserved for
+														// breakpoints in
+	// Java debuggers; should not
+	// appear in any class file
+	final static short	impdep1			= 0xfe;			// reserved for
+	// implementation-dependent
+	// operations within debuggers;
+	// should not appear in any
+	// class file
+	final static short	impdep2			= 0xff;			// reserved for
+	// implementation-dependent
+	// operations within debuggers;
+	// should not appear in any
+	// class file
+
+	final static byte	OFFSETS[]		= new byte[256];
+
+	static {
+		OFFSETS[bipush] = 1; // byte ? value pushes a byte onto the
+		// stack as an integer value
+		OFFSETS[sipush] = 2; // byte1, byte2 ? value pushes a signed
+		// integer (byte1 << 8 + byte2) onto the
+		// stack
+		OFFSETS[ldc] = 1; // index ? value pushes a constant
+		// #index from a constant pool (String,
+		// int, float or class type) onto the
+		// stack
+		OFFSETS[ldc_w] = 2; // indexbyte1, indexbyte2 ? value pushes
+		// a constant #index from a constant
+		// pool (String, int, float or class
+		// type) onto the stack (wide index is
+		// constructed as indexbyte1 << 8 +
+		// indexbyte2)
+		OFFSETS[ldc2_w] = 2; // indexbyte1, indexbyte2 ? value pushes
+		// a constant #index from a constant
+		// pool (double or long) onto the stack
+		// (wide index is constructed as
+		// indexbyte1 << 8 + indexbyte2)
+		OFFSETS[iload] = 1; // index ? value loads an int value from
+		// a variable #index
+		OFFSETS[lload] = 1; // index ? value load a long value from
+		// a local variable #index
+		OFFSETS[fload] = 1; // index ? value loads a float value
+		// from a local variable #index
+		OFFSETS[dload] = 1; // index ? value loads a double value
+		// from a local variable #index
+		OFFSETS[aload] = 1; // index ? objectref loads a reference
+		// onto the stack from a local variable
+		// #index
+		OFFSETS[istore] = 1; // index value ? store int value into
+		// variable #index
+		OFFSETS[lstore] = 1; // index value ? store a long value in a
+		// local variable #index
+		OFFSETS[fstore] = 1; // index value ? stores a float value
+		// into a local variable #index
+		OFFSETS[dstore] = 1; // index value ? stores a double value
+		// into a local variable #index
+		OFFSETS[iinc] = 2; // index, const [No change] increment
+		// local variable #index by signed byte
+		// const
+		OFFSETS[ifeq] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is 0, branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[astore] = 1; // index objectref ? stores a reference
+		// into a local variable #index
+		OFFSETS[ifne] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is not 0, branch to instruction
+		// at branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[iflt] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is less than 0, branch to
+		// instruction at branchoffset (signed
+		// short constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[ifge] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is greater than or equal to 0,
+		// branch to instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[ifgt] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is greater than 0, branch to
+		// instruction at branchoffset (signed
+		// short constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[ifle] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is less than or equal to 0,
+		// branch to instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[if_icmpeq] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if ints are equal,
+		// branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[if_icmpne] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if ints are not equal,
+		// branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[if_icmplt] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if value1 is less than
+		// value2, branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[if_icmpge] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if value1 is greater
+		// than or equal to value2, branch
+		// to instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[if_icmpgt] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if value1 is greater
+		// than value2, branch to
+		// instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[if_icmple] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if value1 is less than
+		// or equal to value2, branch to
+		// instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[if_acmpeq] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if references are equal,
+		// branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[if_acmpne] = 2; // branchbyte1, branchbyte2 value1,
+		// value2 ? if references are not
+		// equal, branch to instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[goto_] = 2; // branchbyte1, branchbyte2 [no change]
+		// goes to another instruction at
+		// branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[jsr] = 2; // branchbyte1, branchbyte2 ? address
+		// jump to subroutine at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2) and place the return
+		// address on the stack
+		OFFSETS[ret] = 1; // index [No change] continue execution
+		// from address taken from a local
+		// variable #index (the asymmetry with
+		// jsr is intentional)
+		OFFSETS[tableswitch] = -1; // [0-3 bytes padding],
+		// defaultbyte1, defaultbyte2,
+		// defaultbyte3, defaultbyte4,
+		// lowbyte1, lowbyte2, lowbyte3,
+		// lowbyte4, highbyte1,
+		// highbyte2, highbyte3,
+		// highbyte4, jump offsets...
+		// index ? continue execution
+		// from an address in the table
+		// at offset index
+		OFFSETS[lookupswitch] = -1; // <0-3 bytes padding>,
+		// defaultbyte1, defaultbyte2,
+		// defaultbyte3, defaultbyte4,
+		// npairs1, npairs2, npairs3,
+		// npairs4, match-offset
+		// pairs... key ? a target
+		// address is looked up from a
+		// table using a key and
+		// execution continues from the
+		// instruction at that address
+		OFFSETS[getstatic] = 2; // index1, index2 ? value gets a
+		// static field value of a class,
+		// where the field is identified by
+		// field reference in the constant
+		// pool index (index1 << 8 + index2)
+		OFFSETS[putstatic] = 2; // indexbyte1, indexbyte2 value ?
+		// set static field to value in a
+		// class, where the field is
+		// identified by a field reference
+		// index in constant pool
+		// (indexbyte1 << 8 + indexbyte2)
+		OFFSETS[getfield] = 2; // index1, index2 objectref ? value
+		// gets a field value of an object
+		// objectref, where the field is
+		// identified by field reference in
+		// the constant pool index (index1
+		// << 8 + index2)
+		OFFSETS[putfield] = 2; // indexbyte1, indexbyte2 objectref,
+		// value ? set field to value in an
+		// object objectref, where the field
+		// is identified by a field
+		// reference index in constant pool
+		// (indexbyte1 << 8 + indexbyte2)
+		OFFSETS[invokevirtual] = 2; // indexbyte1, indexbyte2
+		// objectref, [arg1, arg2, ...]
+		// ? invoke virtual method on
+		// object objectref, where the
+		// method is identified by
+		// method reference index in
+		// constant pool (indexbyte1 <<
+		// 8 + indexbyte2)
+		OFFSETS[invokespecial] = 2; // indexbyte1, indexbyte2
+		// objectref, [arg1, arg2, ...]
+		// ? invoke instance method on
+		// object objectref, where the
+		// method is identified by
+		// method reference index in
+		// constant pool (indexbyte1 <<
+		// 8 + indexbyte2)
+		OFFSETS[invokestatic] = 2; // indexbyte1, indexbyte2 [arg1,
+		// arg2, ...] ? invoke a static
+		// method, where the method is
+		// identified by method
+		// reference index in constant
+		// pool (indexbyte1 << 8 +
+		// indexbyte2)
+		OFFSETS[invokeinterface] = 2; // indexbyte1, indexbyte2,
+		// count, 0 objectref,
+		// [arg1, arg2, ...] ?
+		// invokes an interface
+		// method on object
+		// objectref, where the
+		// interface method is
+		// identified by method
+		// reference index in
+		// constant pool (indexbyte1
+		// << 8 + indexbyte2)
+		OFFSETS[new_] = 2; // indexbyte1, indexbyte2 ? objectref
+		// creates new object of type identified
+		// by class reference in constant pool
+		// index (indexbyte1 << 8 + indexbyte2)
+		OFFSETS[newarray] = 1; // atype count ? arrayref creates
+		// new array with count elements of
+		// primitive type identified by
+		// atype
+		OFFSETS[anewarray] = 2; // indexbyte1, indexbyte2 count ?
+		// arrayref creates a new array of
+		// references of length count and
+		// component type identified by the
+		// class reference index (indexbyte1
+		// << 8 + indexbyte2) in the
+		// constant pool
+		OFFSETS[checkcast] = 2; // indexbyte1, indexbyte2 objectref
+		// ? objectref checks whether an
+		// objectref is of a certain type,
+		// the class reference of which is
+		// in the constant pool at index
+		// (indexbyte1 << 8 + indexbyte2)
+		OFFSETS[instanceof_] = 2; // indexbyte1, indexbyte2 objectref
+		// ? result determines if an object
+		// objectref is of a given type,
+		// identified by class reference
+		// index in constant pool
+		// (indexbyte1 << 8 + indexbyte2)
+		OFFSETS[wide] = 3; // opcode, indexbyte1, indexbyte2
+		OFFSETS[multianewarray] = 3; // indexbyte1, indexbyte2,
+		// dimensions count1,
+		// [count2,...] ? arrayref
+		// create a new array of
+		// dimensions dimensions with
+		// elements of type identified
+		// by class reference in
+		// constant pool index
+		// (indexbyte1 << 8 +
+		// indexbyte2); the sizes of
+		// each dimension is identified
+		// by count1, [count2, etc]
+		OFFSETS[ifnull] = 2; // branchbyte1, branchbyte2 value ? if
+		// value is null, branch to instruction
+		// at branchoffset (signed short
+		// constructed from unsigned bytes
+		// branchbyte1 << 8 + branchbyte2)
+		OFFSETS[ifnonnull] = 2; // branchbyte1, branchbyte2 value ?
+		// if value is not null, branch to
+		// instruction at branchoffset
+		// (signed short constructed from
+		// unsigned bytes branchbyte1 << 8 +
+		// branchbyte2)
+		OFFSETS[goto_w] = 4; // branchbyte1, branchbyte2,
+		// branchbyte3, branchbyte4 [no change]
+		// goes to another instruction at
+		// branchoffset (signed int constructed
+		// from unsigned bytes branchbyte1 << 24
+		// + branchbyte2 << 16 + branchbyte3 <<
+		// 8 + branchbyte4)
+		OFFSETS[jsr_w] = 4; // branchbyte1, branchbyte2,
+		// branchbyte3, branchbyte4 ? address
+		// jump to subroutine at branchoffset
+		// (signed int constructed from unsigned
+		// bytes branchbyte1 << 24 + branchbyte2
+		// << 16 + branchbyte3 << 8 +
+		// branchbyte4) and place the return
+		// address on the stack
+	}
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/PreprocessResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/PreprocessResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/PreprocessResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,37 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public class PreprocessResource extends AbstractResource {
+    final Resource  resource;
+    final Processor processor;
+
+    public PreprocessResource(Processor processor, Resource r) {
+        super(r.lastModified());
+        this.processor = processor;
+        this.resource = r;
+        extra = resource.getExtra();
+    }
+
+    protected byte[] getBytes() throws IOException {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream(2000);
+        OutputStreamWriter osw = new OutputStreamWriter(bout);
+        PrintWriter pw = new PrintWriter(osw);
+        InputStream in = resource.openInputStream();
+        try {
+            BufferedReader rdr = new BufferedReader(new InputStreamReader(in));
+            String line = rdr.readLine();
+            while (line != null) {
+                line = processor.getReplacer().process(line);
+                pw.println(line);
+                line = rdr.readLine();
+            }
+            pw.flush();
+            byte [] data= bout.toByteArray();
+            return data;
+                
+        } finally {
+            in.close();
+        }        
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Processor.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Processor.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Processor.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,1105 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.bnd.make.*;
+import aQute.bnd.service.*;
+import aQute.libg.header.*;
+import aQute.libg.reporter.*;
+
+public class Processor implements Reporter, Constants, Closeable {
+    // TODO handle include files out of date
+    public static String    DEFAULT_PLUGINS = "";                            // "aQute.lib.spring.SpringComponent";
+    // TODO make splitter skip eagerly whitespace so trim is not necessary
+    public static String    LIST_SPLITTER   = "\\\\?\\s*,\\s*";
+    private List<String>    errors          = new ArrayList<String>();
+    private List<String>    warnings        = new ArrayList<String>();
+    boolean                 pedantic;
+    boolean                 trace;
+    boolean                 exceptions;
+    boolean                 fileMustExist   = true;
+
+    List<Object>            plugins;
+    private File            base            = new File("").getAbsoluteFile();
+    private List<Closeable> toBeClosed      = newList();
+
+    final Properties        properties;
+    private Macro           replacer;
+    private long            lastModified;
+    private File            propertiesFile;
+    private boolean         fixup           = true;
+    long                    modified;
+    Processor               parent;
+    Set<File>               included;
+    CL                      pluginLoader;
+    Collection<String>      filter;
+    HashSet<String>         missingCommand;
+
+    public Processor() {
+        properties = new Properties();
+    }
+
+    public Processor(Properties parent) {
+        properties = new Properties(parent);
+    }
+
+    public Processor(Processor parent) {
+        this(parent.properties);
+        this.parent = parent;
+    }
+
+    public void setParent(Processor processor) {
+        this.parent = processor;
+    }
+
+    public Processor getParent() {
+        return parent;
+    }
+
+    public Processor getTop() {
+        if (parent == null)
+            return this;
+        else
+            return parent.getTop();
+    }
+
+    public void getInfo(Processor processor, String prefix) {
+        if (isFailOk())
+            addAll(warnings, processor.getErrors(), prefix);
+        else
+            addAll(errors, processor.getErrors(), prefix);
+        addAll(warnings, processor.getWarnings(), prefix);
+
+        processor.errors.clear();
+        processor.warnings.clear();
+    }
+
+    public void getInfo(Processor processor) {
+        getInfo(processor, "");
+    }
+
+    private <T> void addAll(List<String> to, List<? extends T> from,
+            String prefix) {
+        for (T x : from) {
+            to.add(prefix + x);
+        }
+    }
+
+    public void warning(String string, Object... args) {
+        String s = String.format(string, args);
+        if (!warnings.contains(s))
+            warnings.add(s);
+    }
+
+    public void error(String string, Object... args) {
+        if (isFailOk())
+            warning(string, args);
+        else {
+            String s = String.format(string, args);
+            if (!errors.contains(s))
+                errors.add(s);
+        }
+    }
+
+    public void error(String string, Throwable t, Object... args) {
+        if (isFailOk())
+            warning(string + ": " + t, args);
+        else {
+            String s = String.format(string, args);
+            if (!errors.contains(s))
+                errors.add(s);
+        }
+        if (exceptions)
+            t.printStackTrace();
+    }
+
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    public Map<String, Map<String, String>> parseHeader(String value) {
+        return parseHeader(value, this);
+    }
+
+    /**
+     * Standard OSGi header parser.
+     * 
+     * @param value
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    static public Map<String, Map<String, String>> parseHeader(String value,
+            Processor logger) {
+        return OSGiHeader.parseHeader(value, logger);
+    }
+
+    Map<String, Map<String, String>> getClauses(String header) {
+        return parseHeader(getProperty(header));
+    }
+
+    public void addClose(Closeable jar) {
+        toBeClosed.add(jar);
+    }
+
+    /**
+     * Remove all entries from a map that start with a specific prefix
+     * 
+     * @param <T>
+     * @param source
+     * @param prefix
+     * @return
+     */
+    static <T> Map<String, T> removeKeys(Map<String, T> source, String prefix) {
+        Map<String, T> temp = new TreeMap<String, T>(source);
+        for (Iterator<String> p = temp.keySet().iterator(); p.hasNext();) {
+            String pack = (String) p.next();
+            if (pack.startsWith(prefix))
+                p.remove();
+        }
+        return temp;
+    }
+
+    public void progress(String s, Object... args) {
+        // System.out.println(s);
+    }
+
+    public boolean isPedantic() {
+        return pedantic;
+    }
+
+    public void setPedantic(boolean pedantic) { // System.out.println("Set
+        // pedantic: " + pedantic + " "
+        // + this );
+        this.pedantic = pedantic;
+    }
+
+    public static File getFile(File base, String file) {
+        File f = new File(file);
+        if (f.isAbsolute())
+            return f;
+        int n;
+
+        f = base.getAbsoluteFile();
+        while ((n = file.indexOf('/')) > 0) {
+            String first = file.substring(0, n);
+            file = file.substring(n + 1);
+            if (first.equals(".."))
+                f = f.getParentFile();
+            else
+                f = new File(f, first);
+        }
+        return new File(f, file).getAbsoluteFile();
+    }
+
+    public File getFile(String file) {
+        return getFile(base, file);
+    }
+
+    /**
+     * Return a list of plugins that implement the given class.
+     * 
+     * @param clazz
+     *            Each returned plugin implements this class/interface
+     * @return A list of plugins
+     */
+    public <T> List<T> getPlugins(Class<T> clazz) {
+        List<T> l = new ArrayList<T>();
+        List<Object> all = getPlugins();
+        for (Object plugin : all) {
+            if (clazz.isInstance(plugin))
+                l.add(clazz.cast(plugin));
+        }
+        return l;
+    }
+
+    /**
+     * Return a list of plugins. Plugins are defined with the -plugin command.
+     * They are class names, optionally associated with attributes. Plugins can
+     * implement the Plugin interface to see these attributes.
+     * 
+     * Any object can be a plugin.
+     * 
+     * @return
+     */
+    protected List<Object> getPlugins() {
+        // if (parent != null) {
+        // List<Object> val = parent.getPlugins();
+        // getInfo(parent);
+        // return val;
+        // }
+        if (this.plugins != null)
+            return this.plugins;
+
+        missingCommand = new HashSet<String>();
+        String spe = getProperty(Analyzer.PLUGIN, DEFAULT_PLUGINS);
+        Map<String, Map<String, String>> plugins = parseHeader(spe);
+        List<Object> list = new ArrayList<Object>();
+
+        // Add the default plugins. Only if non is specified
+        // will they be removed.
+        list.add(new MakeBnd());
+        list.add(new MakeCopy());
+        list.add(new ServiceComponent());
+
+        for (Map.Entry<String, Map<String, String>> entry : plugins.entrySet()) {
+            String key = (String) entry.getKey();
+            if (key.equals(NONE))
+                return this.plugins = newList();
+
+            try {
+                CL loader = getLoader();
+                String path = entry.getValue().get(PATH_DIRECTIVE);
+                if (path != null) {
+                    File f = getFile(path).getAbsoluteFile();
+                    loader.add(f.toURL());
+                }
+
+                trace("Using plugin %s", key);
+
+                // Plugins could use the same class with different
+                // parameters so we could have duplicate names Remove
+                // the ! added by the parser to make each name unique.
+                key = removeDuplicateMarker(key);
+
+                try {
+                    Class<?> c = (Class<?>) loader.loadClass(key);
+                    Object plugin = c.newInstance();
+                    if (plugin instanceof Plugin) {
+                        ((Plugin) plugin).setProperties(entry.getValue());
+                        ((Plugin) plugin).setReporter(this);
+                    }
+                    list.add(plugin);
+                } catch (Throwable t) {
+                    // We can defer the error if the plugin specifies 
+                    // a command name. In that case, we'll verify that 
+                    // a bnd file does not contain any references to a plugin
+                    // command. The reason this feature was added was
+                    // to compile plugin classes with the same build.
+                    String commands = entry.getValue().get(COMMAND_DIRECTIVE);
+                    if (commands == null)
+                        error("Problem loading the plugin: %s exception: (%s)",
+                                key, t);
+                    else {
+                        Collection<String> cs = split(commands);
+                        missingCommand.addAll(cs);
+                    }
+                }
+            } catch (Throwable e) {
+                error("Problem loading the plugin: " + key + " exception: " + e);
+            }
+        }
+        return this.plugins = list;
+    }
+
+    public boolean isFailOk() {
+        String v = getProperty(Analyzer.FAIL_OK, null);
+        return v != null && v.equalsIgnoreCase("true");
+    }
+
+    public File getBase() {
+        return base;
+    }
+
+    public void setBase(File base) {
+        this.base = base;
+    }
+
+    public void clear() {
+        errors.clear();
+        warnings.clear();
+    }
+
+    public void trace(String msg, Object... parms) {
+        if (trace) {
+            System.out.printf("# " + msg + "\n", parms);
+        }
+    }
+
+    public <T> List<T> newList() {
+        return new ArrayList<T>();
+    }
+
+    public <T> Set<T> newSet() {
+        return new TreeSet<T>();
+    }
+
+    public static <K, V> Map<K, V> newMap() {
+        return new LinkedHashMap<K, V>();
+    }
+
+    public static <K, V> Map<K, V> newHashMap() {
+        return new HashMap<K, V>();
+    }
+
+    public <T> List<T> newList(Collection<T> t) {
+        return new ArrayList<T>(t);
+    }
+
+    public <T> Set<T> newSet(Collection<T> t) {
+        return new TreeSet<T>(t);
+    }
+
+    public <K, V> Map<K, V> newMap(Map<K, V> t) {
+        return new LinkedHashMap<K, V>(t);
+    }
+
+    public void close() {
+        for (Closeable c : toBeClosed) {
+            try {
+                c.close();
+            } catch (IOException e) {
+                // Who cares?
+            }
+        }
+        toBeClosed = null;
+    }
+
+    public String _basedir(String args[]) {
+        if (base == null)
+            throw new IllegalArgumentException("No base dir set");
+
+        return base.getAbsolutePath();
+    }
+
+    /**
+     * Property handling ...
+     * 
+     * @return
+     */
+
+    public Properties getProperties() {
+        if (fixup) {
+            fixup = false;
+            begin();
+        }
+
+        return properties;
+    }
+
+    public String getProperty(String key) {
+        return getProperty(key, null);
+    }
+
+    public void mergeProperties(File file, boolean override) {
+        if (file.isFile()) {
+            try {
+                Properties properties = loadProperties(file);
+                mergeProperties(properties, override);
+            } catch (Exception e) {
+                error("Error loading properties file: " + file);
+            }
+        } else {
+            if (!file.exists())
+                error("Properties file does not exist: " + file);
+            else
+                error("Properties file must a file, not a directory: " + file);
+        }
+    }
+
+    public void mergeProperties(Properties properties, boolean override) {
+        for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();) {
+            String key = (String) e.nextElement();
+            String value = properties.getProperty(key);
+            if (override || !getProperties().containsKey(key))
+                setProperty(key, value);
+        }
+    }
+
+    public void setProperties(Properties properties) {
+        doIncludes(getBase(), properties, new HashSet<String>());
+        this.properties.putAll(properties);
+    }
+
+    public void addProperties(File file) throws Exception {
+        addIncluded(file);
+        Properties p = loadProperties(file);
+        setProperties(p);
+    }
+
+    public synchronized void addIncluded(File file) {
+        if (included == null)
+            included = new HashSet<File>();
+        included.add(file);
+    }
+
+    /**
+     * Inspect the properties and if you find -includes parse the line included
+     * manifest files or properties files. The files are relative from the given
+     * base, this is normally the base for the analyzer.
+     * 
+     * @param ubase
+     * @param p
+     * @param done
+     * @throws IOException
+     */
+    private void doIncludes(File ubase, Properties p, Set<String> done) {
+        String includes = p.getProperty(INCLUDE);
+        if (includes != null) {
+            includes = getReplacer().process(includes);
+            p.remove(INCLUDE);
+            Collection<String> clauses = parseHeader(includes).keySet();
+
+            for (String value : clauses) {
+                boolean fileMustExist = true;
+                boolean overwrite = true;
+                while (true) {
+                    if (value.startsWith("-")) {
+                        fileMustExist = false;
+                        value = value.substring(1).trim();
+                    } else if (value.startsWith("~")) {
+                        // Overwrite properties!
+                        overwrite = false;
+                        value = value.substring(1).trim();
+                    } else
+                        break;
+                }
+                try {
+                    File file = getFile(ubase, value).getAbsoluteFile();
+                    if (file.isFile()) {
+                        if (included != null && included.contains(file)) {
+                            error("Cyclic include of " + file);
+                        } else {
+                            addIncluded(file);
+                            updateModified(file.lastModified(), "Include "
+                                    + value);
+                            InputStream in = new FileInputStream(file);
+                            Properties sub;
+                            if (file.getName().toLowerCase().endsWith(".mf")) {
+                                sub = getManifestAsProperties(in);
+                            } else
+                                sub = loadProperties(in, file.getAbsolutePath());
+                            in.close();
+
+                            doIncludes(file.getParentFile(), sub, done);
+                            // make sure we do not override properties
+                            if (!overwrite)
+                                sub.keySet().removeAll(p.keySet());
+                            p.putAll(sub);
+                        }
+                    } else {
+                        if (fileMustExist)
+                            error("Included file "
+                                    + file
+                                    + (file.exists() ? " does not exist"
+                                            : " is directory"));
+                    }
+                } catch (IOException e) {
+                    if (fileMustExist)
+                        error("Error in processing included file: " + value, e);
+                }
+            }
+        }
+    }
+
+    public void unsetProperty(String string) {
+        getProperties().remove(string);
+
+    }
+
+    public boolean refresh() {
+        plugins = null; // We always refresh our plugins
+
+        if (propertiesFile == null)
+            return false;
+
+        boolean changed = false;
+        if (included != null) {
+            for (File file : included) {
+
+                if (file.lastModified() > modified) {
+                    changed = true;
+                    break;
+                }
+            }
+        }
+
+        // System.out.println("Modified " + modified + " file: "
+        // + propertiesFile.lastModified() + " diff "
+        // + (modified - propertiesFile.lastModified()));
+
+        // Date last = new Date(propertiesFile.lastModified());
+        // Date current = new Date(modified);
+        changed |= modified < propertiesFile.lastModified();
+        if (changed) {
+            included = null;
+            properties.clear();
+            setProperties(propertiesFile, base);
+            propertiesChanged();
+            return true;
+        }
+        return false;
+    }
+
+    public void propertiesChanged() {
+    }
+
+    /**
+     * Set the properties by file. Setting the properties this way will also set
+     * the base for this analyzer. After reading the properties, this will call
+     * setProperties(Properties) which will handle the includes.
+     * 
+     * @param propertiesFile
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    public void setProperties(File propertiesFile) throws IOException {
+        propertiesFile = propertiesFile.getAbsoluteFile();
+        setProperties(propertiesFile, propertiesFile.getParentFile());
+    }
+
+    public void setProperties(File propertiesFile, File base) {
+        this.propertiesFile = propertiesFile.getAbsoluteFile();
+        setBase(base);
+        try {
+            if (propertiesFile.isFile()) {
+                // System.out.println("Loading properties " + propertiesFile);
+                long modified = propertiesFile.lastModified();
+                if (modified > System.currentTimeMillis() + 100) {
+                    System.out.println("Huh? This is in the future "
+                            + propertiesFile);
+                    this.modified = System.currentTimeMillis();
+                } else
+                    this.modified = modified;
+
+                included = null;
+                Properties p = loadProperties(propertiesFile);
+                setProperties(p);
+            } else {
+                if (fileMustExist) {
+                    error("No such properties file: " + propertiesFile);
+                }
+            }
+        } catch (IOException e) {
+            error("Could not load properties " + propertiesFile);
+        }
+    }
+
+    protected void begin() {
+        if (isTrue(getProperty(PEDANTIC)))
+            setPedantic(true);
+    }
+
+    public static boolean isTrue(String value) {
+        return "true".equalsIgnoreCase(value);
+    }
+
+    /**
+     * Get a property with a proper default
+     * 
+     * @param headerName
+     * @param deflt
+     * @return
+     */
+    public String getProperty(String key, String deflt) {
+        String value;
+
+        if (filter != null && filter.contains(key)) {
+            value = (String) getProperties().get(key);
+        } else
+            value = getProperties().getProperty(key);
+
+        if (value != null)
+            return getReplacer().process(value);
+        else if (deflt != null)
+            return getReplacer().process(deflt);
+        else
+            return null;
+    }
+
+    /**
+     * Helper to load a properties file from disk.
+     * 
+     * @param file
+     * @return
+     * @throws IOException
+     */
+    public Properties loadProperties(File file) throws IOException {
+        updateModified(file.lastModified(), "Properties file: " + file);
+        InputStream in = new FileInputStream(file);
+        Properties p = loadProperties(in, file.getAbsolutePath());
+        in.close();
+        return p;
+    }
+
+    Properties loadProperties(InputStream in, String name) throws IOException {
+        int n = name.lastIndexOf('/');
+        if (n > 0)
+            name = name.substring(0, n);
+        if (name.length() == 0)
+            name = ".";
+
+        try {
+            Properties p = new Properties();
+            p.load(in);
+            return replaceAll(p, "\\$\\{\\.\\}", name);
+        } catch (Exception e) {
+            error("Error during loading properties file: " + name + ", error:"
+                    + e);
+            return new Properties();
+        }
+    }
+
+    /**
+     * Replace a string in all the values of the map. This can be used to
+     * preassign variables that change. I.e. the base directory ${.} for a
+     * loaded properties
+     */
+
+    public static Properties replaceAll(Properties p, String pattern,
+            String replacement) {
+        Properties result = new Properties();
+        for (Iterator<Map.Entry<Object, Object>> i = p.entrySet().iterator(); i
+                .hasNext();) {
+            Map.Entry<Object, Object> entry = i.next();
+            String key = (String) entry.getKey();
+            String value = (String) entry.getValue();
+            value = value.replaceAll(pattern, replacement);
+            result.put(key, value);
+        }
+        return result;
+    }
+
+    /**
+     * Merge the attributes of two maps, where the first map can contain
+     * wildcarded names. The idea is that the first map contains patterns (for
+     * example *) with a set of attributes. These patterns are matched against
+     * the found packages in actual. If they match, the result is set with the
+     * merged set of attributes. It is expected that the instructions are
+     * ordered so that the instructor can define which pattern matches first.
+     * Attributes in the instructions override any attributes from the actual.<br/>
+     * 
+     * A pattern is a modified regexp so it looks like globbing. The * becomes a .*
+     * just like the ? becomes a .?. '.' are replaced with \\. Additionally, if
+     * the pattern starts with an exclamation mark, it will remove that matches
+     * for that pattern (- the !) from the working set. So the following
+     * patterns should work:
+     * <ul>
+     * <li>com.foo.bar</li>
+     * <li>com.foo.*</li>
+     * <li>com.foo.???</li>
+     * <li>com.*.[^b][^a][^r]</li>
+     * <li>!com.foo.* (throws away any match for com.foo.*)</li>
+     * </ul>
+     * Enough rope to hang the average developer I would say.
+     * 
+     * 
+     * @param instructions
+     *            the instructions with patterns. A
+     * @param actual
+     *            the actual found packages
+     */
+
+    public static Map<String, Map<String, String>> merge(String type,
+            Map<String, Map<String, String>> instructions,
+            Map<String, Map<String, String>> actual, Set<String> superfluous,
+            Map<String, Map<String, String>> ignored) {
+        Map<String, Map<String, String>> toVisit = new HashMap<String, Map<String, String>>(
+                actual); // we do not want to ruin our
+        // original
+        Map<String, Map<String, String>> result = newMap();
+        for (Iterator<String> i = instructions.keySet().iterator(); i.hasNext();) {
+            String instruction = i.next();
+            String originalInstruction = instruction;
+
+            Map<String, String> instructedAttributes = instructions
+                    .get(instruction);
+
+            // Check if we have a fixed (starts with '=') or a
+            // duplicate name. A fixed name is added to the output without
+            // checking against the contents. Duplicates are marked
+            // at the end. In that case we do not pick up any contained
+            // information but just add them to the output including the
+            // marker.
+            if (instruction.startsWith("=")) {
+                result.put(instruction.substring(1), instructedAttributes);
+                superfluous.remove(originalInstruction);
+                continue;
+            }
+            if (isDuplicate(instruction)) {
+                result.put(instruction, instructedAttributes);
+                superfluous.remove(originalInstruction);
+                continue;
+            }
+
+            Instruction instr = Instruction.getPattern(instruction);
+
+            for (Iterator<String> p = toVisit.keySet().iterator(); p.hasNext();) {
+                String packageName = p.next();
+
+                if (instr.matches(packageName)) {
+                    superfluous.remove(originalInstruction);
+                    if (!instr.isNegated()) {
+                        Map<String, String> newAttributes = new HashMap<String, String>();
+                        newAttributes.putAll(actual.get(packageName));
+                        newAttributes.putAll(instructedAttributes);
+                        result.put(packageName, newAttributes);
+                    } else if (ignored != null) {
+                        ignored.put(packageName, new HashMap<String, String>());
+                    }
+                    p.remove(); // Can never match again for another pattern
+                }
+            }
+
+        }
+        return result;
+    }
+
+    /**
+     * Print a standard Map based OSGi header.
+     * 
+     * @param exports
+     *            map { name => Map { attribute|directive => value } }
+     * @return the clauses
+     */
+    public static String printClauses(Map<String, Map<String, String>> exports,
+            String allowedDirectives) {
+        return printClauses(exports, allowedDirectives, false);
+    }
+
+    public static String printClauses(Map<String, Map<String, String>> exports,
+            String allowedDirectives, boolean checkMultipleVersions) {
+        StringBuffer sb = new StringBuffer();
+        String del = "";
+        for (Iterator<String> i = exports.keySet().iterator(); i.hasNext();) {
+            String name = i.next();
+            Map<String, String> clause = exports.get(name);
+
+            // We allow names to be duplicated in the input
+            // by ending them with '~'. This is necessary to use
+            // the package names as keys. However, we remove these
+            // suffixes in the output so that you can set multiple
+            // exports with different attributes.
+            String outname = removeDuplicateMarker(name);
+            sb.append(del);
+            sb.append(outname);
+            printClause(clause, allowedDirectives, sb);
+            del = ",";
+        }
+        return sb.toString();
+    }
+
+    public static void printClause(Map<String, String> map,
+            String allowedDirectives, StringBuffer sb) {
+
+        for (Iterator<String> j = map.keySet().iterator(); j.hasNext();) {
+            String key = j.next();
+
+            // Skip directives we do not recognize
+            if (!key.startsWith("x-")
+                    && key.endsWith(":")
+                    && (allowedDirectives == null || allowedDirectives
+                            .indexOf(key) < 0))
+                continue;
+
+            String value = ((String) map.get(key)).trim();
+            sb.append(";");
+            sb.append(key);
+            sb.append("=");
+
+            boolean clean = (value.length() >= 2 && value.charAt(0) == '"' && value
+                    .charAt(value.length() - 1) == '"')
+                    || Verifier.TOKEN.matcher(value).matches();
+            if (!clean)
+                sb.append("\"");
+            sb.append(value);
+            if (!clean)
+                sb.append("\"");
+        }
+    }
+
+    public Macro getReplacer() {
+        if (replacer == null)
+            return replacer = new Macro(getProperties(), this,
+                    getMacroDomains());
+        else
+            return replacer;
+    }
+
+    /**
+     * This should be overridden by subclasses to add extra macro command
+     * domains on the search list.
+     * 
+     * @return
+     */
+    protected Object[] getMacroDomains() {
+        return new Object[] {};
+    }
+
+    /**
+     * Return the properties but expand all macros. This always returns a new
+     * Properties object that can be used in any way.
+     * 
+     * @return
+     */
+    public Properties getFlattenedProperties() {
+        return getReplacer().getFlattenedProperties();
+
+    }
+
+    public void updateModified(long time, String reason) {
+        if (time > lastModified) {
+            lastModified = time;
+        }
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    /**
+     * Add or override a new property.
+     * 
+     * @param key
+     * @param value
+     */
+    public void setProperty(String key, String value) {
+        checkheader: for (int i = 0; i < headers.length; i++) {
+            if (headers[i].equalsIgnoreCase(value)) {
+                value = headers[i];
+                break checkheader;
+            }
+        }
+        getProperties().put(key, value);
+    }
+
+    /**
+     * Read a manifest but return a properties object.
+     * 
+     * @param in
+     * @return
+     * @throws IOException
+     */
+    public static Properties getManifestAsProperties(InputStream in)
+            throws IOException {
+        Properties p = new Properties();
+        Manifest manifest = new Manifest(in);
+        for (Iterator<Object> it = manifest.getMainAttributes().keySet()
+                .iterator(); it.hasNext();) {
+            Attributes.Name key = (Attributes.Name) it.next();
+            String value = manifest.getMainAttributes().getValue(key);
+            p.put(key.toString(), value);
+        }
+        return p;
+    }
+
+    public File getPropertiesFile() {
+        return propertiesFile;
+    }
+
+    public void setFileMustExist(boolean mustexist) {
+        fileMustExist = mustexist;
+    }
+
+    static public String read(InputStream in) throws Exception {
+        InputStreamReader ir = new InputStreamReader(in);
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            char chars[] = new char[1000];
+            int size = ir.read(chars);
+            while (size > 0) {
+                sb.append(chars, 0, size);
+                size = ir.read(chars);
+            }
+        } finally {
+            ir.close();
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Join a list.
+     * 
+     * @param args
+     * @return
+     */
+    public static String join(Collection<?> list, String delimeter) {
+        if (list == null)
+            return "";
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+        for (Object item : list) {
+            sb.append(del);
+            sb.append(item);
+            del = delimeter;
+        }
+        return sb.toString();
+    }
+
+    public static String join(Object[] list, String delimeter) {
+        if (list == null)
+            return "";
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+        for (Object item : list) {
+            sb.append(del);
+            sb.append(item);
+            del = delimeter;
+        }
+        return sb.toString();
+    }
+
+    public static String join(Collection<?> list) {
+        return join(list, ",");
+    }
+
+    public static void split(String s, Collection<String> set) {
+
+        String elements[] = s.trim().split(LIST_SPLITTER);
+        for (String element : elements) {
+            if (element.length() > 0)
+                set.add(element);
+        }
+    }
+
+    public static Collection<String> split(String s) {
+        return split(s, LIST_SPLITTER);
+    }
+
+    public static Collection<String> split(String s, String splitter) {
+        if (s != null)
+            s = s.trim();
+        if (s == null || s.trim().length() == 0)
+            return Collections.emptyList();
+
+        return Arrays.asList(s.split(splitter));
+    }
+
+    public boolean isExceptions() {
+        return exceptions;
+    }
+
+    public void setExceptions(boolean exceptions) {
+        this.exceptions = exceptions;
+    }
+
+    /**
+     * Make the file short if it is inside our base directory, otherwise long.
+     * 
+     * @param f
+     * @return
+     */
+    public String normalize(String f) {
+        if (f.startsWith(base.getAbsolutePath() + "/"))
+            return f.substring(base.getAbsolutePath().length() + 1);
+        else
+            return f;
+    }
+
+    public String normalize(File f) {
+        return normalize(f.getAbsolutePath());
+    }
+
+    public static String removeDuplicateMarker(String key) {
+        int i = key.length() - 1;
+        while (i >= 0 && key.charAt(i) == DUPLICATE_MARKER)
+            --i;
+
+        return key.substring(0, i + 1);
+    }
+
+    public static boolean isDuplicate(String name) {
+        return name.length() > 0
+                && name.charAt(name.length() - 1) == DUPLICATE_MARKER;
+    }
+
+    public void setTrace(boolean x) {
+        trace = x;
+    }
+
+    static class CL extends URLClassLoader {
+
+        CL() {
+            super(new URL[0], Processor.class.getClassLoader());
+        }
+
+        void add(URL url) {
+            URL urls[] = getURLs();
+            for (URL u : urls) {
+                if (u.equals(url))
+                    return;
+            }
+            super.addURL(url);
+        }
+
+        public Class<?> loadClass(String name) throws NoClassDefFoundError {
+            try {
+                Class<?> c = super.loadClass(name);
+                return c;
+            } catch (Throwable t) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(name);
+                sb.append(" not found, parent:  ");
+                sb.append(getParent());
+                sb.append(" urls:");
+                sb.append(Arrays.toString(getURLs()));
+                sb.append(" exception:");
+                sb.append(t);
+                throw new NoClassDefFoundError(sb.toString());
+            }
+        }
+    }
+
+    private CL getLoader() {
+        if (pluginLoader == null) {
+            pluginLoader = new CL();
+        }
+        return pluginLoader;
+    }
+
+    public boolean exists() {
+        return base != null && base.exists();
+    }
+
+    public boolean isOk() {
+        return isFailOk() || (getErrors().size() == 0);
+    }
+
+    public boolean isPerfect() {
+        return getErrors().size() == 0 && getWarnings().size() == 0;
+    }
+
+    public void setForceLocal(Collection<String> local) {
+        filter = local;
+    }
+
+    public static Map<Instruction, Map<String, String>> replaceWitInstruction(
+            Map<String, Map<String, String>> header, String type) {
+        Map<Instruction, Map<String, String>> map = newMap();
+        for (Iterator<Map.Entry<String, Map<String, String>>> e = header
+                .entrySet().iterator(); e.hasNext();) {
+            Map.Entry<String, Map<String, String>> entry = e.next();
+            String pattern = entry.getKey();
+            Instruction instr = Instruction.getPattern(pattern);
+            String presence = entry.getValue().get(PRESENCE_DIRECTIVE);
+            if ("optional".equals(presence))
+                instr.setOptional();
+            map.put(instr, entry.getValue());
+        }
+        return map;
+    }
+    
+    /**
+     * Answer if the name is a missing plugin's command name. If a bnd file
+     * contains the command name of a plugin, and that plugin is not available, then
+     * an error is reported during manifest calculation. This allows the 
+     * plugin to fail to load when it is not needed.
+     * 
+     * We first get the plugins to ensure it is properly initialized.
+     * 
+     * @param name
+     * @return
+     */
+    public boolean isMissingPlugin(String name) {
+        getPlugins();
+        return missingCommand != null && missingCommand.contains(name);
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Resource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Resource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Resource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,11 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public interface Resource {
+	InputStream openInputStream() throws IOException ;
+	void write(OutputStream out) throws IOException;
+	long lastModified();
+	void setExtra(String extra);
+	String getExtra();	
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/URLResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/URLResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/URLResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,37 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.net.*;
+
+public class URLResource implements Resource {
+	URL	url;
+	String	extra;
+	
+	public URLResource(URL url) {
+		this.url = url;
+	}
+
+	public InputStream openInputStream() throws IOException {
+		return url.openStream();
+	}
+
+	public String toString() {
+		return ":" + url.getPath() + ":";
+	}
+
+	public void write(OutputStream out) throws IOException {
+		FileResource.copy(this, out);
+	}
+
+	public long lastModified() {
+		return -1;
+	}
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Verifier.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Verifier.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/Verifier.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,846 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.libg.qtokens.*;
+
+public class Verifier extends Analyzer {
+
+    Jar                              dot;
+    Manifest                         manifest;
+    Map<String, Map<String, String>> referred                       = newHashMap();
+    Map<String, Map<String, String>> contained                      = newHashMap();
+    Map<String, Set<String>>         uses                           = newHashMap();
+    Map<String, Map<String, String>> mimports;
+    Map<String, Map<String, String>> mdynimports;
+    Map<String, Map<String, String>> mexports;
+    List<Jar>                        bundleClasspath;
+    Map<String, Map<String, String>> ignore                         = newHashMap();                                                         // Packages
+    // to
+    // ignore
+
+    Map<String, Clazz>               classSpace;
+    boolean                          r3;
+    boolean                          usesRequire;
+    boolean                          fragment;
+    Attributes                       main;
+
+    final static Pattern             EENAME                         = Pattern
+                                                                            .compile("CDC-1\\.0/Foundation-1\\.0"
+                                                                                    + "|CDC-1\\.1/Foundation-1\\.1"
+                                                                                    + "|OSGi/Minimum-1\\.[1-9]"
+                                                                                    + "|JRE-1\\.1"
+                                                                                    + "|J2SE-1\\.2"
+                                                                                    + "|J2SE-1\\.3"
+                                                                                    + "|J2SE-1\\.4"
+                                                                                    + "|J2SE-1\\.5"
+                                                                                    + "|JavaSE-1\\.6"
+                                                                                    + "|JavaSE-1\\.7"
+                                                                                    + "|PersonalJava-1\\.1"
+                                                                                    + "|PersonalJava-1\\.2"
+                                                                                    + "|CDC-1\\.0/PersonalBasis-1\\.0"
+                                                                                    + "|CDC-1\\.0/PersonalJava-1\\.0");
+
+    final static Pattern             BUNDLEMANIFESTVERSION          = Pattern
+                                                                            .compile("2");
+    public final static String       SYMBOLICNAME_STRING            = "[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)*";
+    public final static Pattern      SYMBOLICNAME                   = Pattern
+                                                                            .compile(SYMBOLICNAME_STRING);
+
+    public final static String       VERSION_STRING                 = "[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?";
+    public final static Pattern      VERSION                        = Pattern
+                                                                            .compile(VERSION_STRING);
+    final static Pattern             FILTEROP                       = Pattern
+                                                                            .compile("=|<=|>=|~=");
+    public final static Pattern      VERSIONRANGE                   = Pattern
+                                                                            .compile("((\\(|\\[)"
+                                                                                    + VERSION_STRING
+                                                                                    + ","
+                                                                                    + VERSION_STRING
+                                                                                    + "(\\]|\\)))|"
+                                                                                    + VERSION_STRING);
+    final static Pattern             FILE                           = Pattern
+                                                                            .compile("/?[^/\"\n\r\u0000]+(/[^/\"\n\r\u0000]+)*");
+    final static Pattern             WILDCARDPACKAGE                = Pattern
+                                                                            .compile("((\\p{Alnum}|_)+(\\.(\\p{Alnum}|_)+)*(\\.\\*)?)|\\*");
+    public final static Pattern      ISO639                         = Pattern
+                                                                            .compile("[A-Z][A-Z]");
+    public final static Pattern      HEADER_PATTERN                 = Pattern
+                                                                            .compile("[A-Za-z0-9][-a-zA-Z0-9_]+");
+    public final static Pattern      TOKEN                          = Pattern
+                                                                            .compile("[-a-zA-Z0-9_]+");
+
+    public final static Pattern      NUMBERPATTERN                  = Pattern
+                                                                            .compile("\\d+");
+    public final static Pattern      PATHPATTERN                    = Pattern
+                                                                            .compile(".*");
+    public final static Pattern      FQNPATTERN                     = Pattern
+                                                                            .compile(".*");
+    public final static Pattern      URLPATTERN                     = Pattern
+                                                                            .compile(".*");
+    public final static Pattern      ANYPATTERN                     = Pattern
+                                                                            .compile(".*");
+    public final static Pattern      FILTERPATTERN                  = Pattern
+                                                                            .compile(".*");
+    public final static Pattern      TRUEORFALSEPATTERN             = Pattern
+                                                                            .compile("true|false|TRUE|FALSE");
+    public static final Pattern      WILDCARDNAMEPATTERN            = Pattern
+                                                                            .compile(".*");
+    public static final Pattern      BUNDLE_ACTIVATIONPOLICYPATTERN = Pattern
+                                                                            .compile("lazy");
+
+    public final static String       EES[]                          = {
+            "CDC-1.0/Foundation-1.0", "CDC-1.1/Foundation-1.1",
+            "OSGi/Minimum-1.0", "OSGi/Minimum-1.1", "OSGi/Minimum-1.2",
+            "JRE-1.1", "J2SE-1.2", "J2SE-1.3", "J2SE-1.4", "J2SE-1.5",
+            "JavaSE-1.6", "JavaSE-1.7", "PersonalJava-1.1", "PersonalJava-1.2",
+            "CDC-1.0/PersonalBasis-1.0", "CDC-1.0/PersonalJava-1.0" };
+
+    public final static String       OSNAMES[]                      = {
+            "AIX", // IBM
+            "DigitalUnix", // Compaq
+            "Embos", // Segger Embedded Software Solutions
+            "Epoc32", // SymbianOS Symbian OS
+            "FreeBSD", // Free BSD
+            "HPUX", // hp-ux Hewlett Packard
+            "IRIX", // Silicon Graphics
+            "Linux", // Open source
+            "MacOS", // Apple
+            "NetBSD", // Open source
+            "Netware", // Novell
+            "OpenBSD", // Open source
+            "OS2", // OS/2 IBM
+            "QNX", // procnto QNX
+            "Solaris", // Sun (almost an alias of SunOS)
+            "SunOS", // Sun Microsystems
+            "VxWorks", // WindRiver Systems
+            "Windows95", "Win32", "Windows98", "WindowsNT", "WindowsCE",
+            "Windows2000", // Win2000
+            "Windows2003", // Win2003
+            "WindowsXP", "WindowsVista",                           };
+
+    public final static String       PROCESSORNAMES[]               = { "68k", // Motorola
+            // 68000
+            "ARM_LE", // Intel Strong ARM. Deprecated because it does not
+            // specify the endianness. See the following two rows.
+            "arm_le", // Intel Strong ARM Little Endian mode
+            "arm_be", // Intel String ARM Big Endian mode
+            "Alpha", //
+            "ia64n",// Hewlett Packard 64 bit
+            "ia64w",// Hewlett Packard 32 bit mode
+            "Ignite", // psc1k PTSC
+            "Mips", // SGI
+            "PArisc", // Hewlett Packard
+            "PowerPC", // power ppc Motorola/IBM Power PC
+            "Sh4", // Hitachi
+            "Sparc", // SUN
+            "S390", // IBM Mainframe 31 bit
+            "S390x", // IBM Mainframe 64-bit
+            "V850E", // NEC V850E
+            "x86", // pentium i386
+            "i486", // i586 i686 Intel& AMD 32 bit
+            "x86-64",                                              };
+
+    Properties                       properties;
+
+    public Verifier(Jar jar) throws Exception {
+        this(jar, null);
+    }
+
+    public Verifier(Jar jar, Properties properties) throws Exception {
+        this.dot = jar;
+        this.properties = properties;
+        this.manifest = jar.getManifest();
+        if (manifest == null) {
+            manifest = new Manifest();
+            error("This file contains no manifest and is therefore not a bundle");
+        }
+        main = this.manifest.getMainAttributes();
+        verifyHeaders(main);
+        r3 = getHeader(Analyzer.BUNDLE_MANIFESTVERSION) == null;
+        usesRequire = getHeader(Analyzer.REQUIRE_BUNDLE) != null;
+        fragment = getHeader(Analyzer.FRAGMENT_HOST) != null;
+
+        bundleClasspath = getBundleClassPath();
+        mimports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.IMPORT_PACKAGE));
+        mdynimports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.DYNAMICIMPORT_PACKAGE));
+        mexports = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.EXPORT_PACKAGE));
+
+        ignore = parseHeader(manifest.getMainAttributes().getValue(
+                Analyzer.IGNORE_PACKAGE));
+    }
+
+    public Verifier() {
+        // TODO Auto-generated constructor stub
+    }
+
+    private void verifyHeaders(Attributes main) {
+        for (Object element : main.keySet()) {
+            Attributes.Name header = (Attributes.Name) element;
+            String h = header.toString();
+            if (!HEADER_PATTERN.matcher(h).matches())
+                error("Invalid Manifest header: " + h + ", pattern="
+                        + HEADER_PATTERN);
+        }
+    }
+
+    private List<Jar> getBundleClassPath() {
+        List<Jar> list = newList();
+        String bcp = getHeader(Analyzer.BUNDLE_CLASSPATH);
+        if (bcp == null) {
+            list.add(dot);
+        } else {
+            Map<String, Map<String, String>> entries = parseHeader(bcp);
+            for (String jarOrDir : entries.keySet()) {
+                if (jarOrDir.equals(".")) {
+                    list.add(dot);
+                } else {
+                    if (jarOrDir.equals("/"))
+                        jarOrDir = "";
+                    if (jarOrDir.endsWith("/")) {
+                        error("Bundle-Classpath directory must not end with a slash: "
+                                + jarOrDir);
+                        jarOrDir = jarOrDir.substring(0, jarOrDir.length() - 1);
+                    }
+
+                    Resource resource = dot.getResource(jarOrDir);
+                    if (resource != null) {
+                        try {
+                            Jar sub = new Jar(jarOrDir);
+                            addClose(sub);
+                            EmbeddedResource.build(sub, resource);
+                            if (!jarOrDir.endsWith(".jar"))
+                                warning("Valid JAR file on Bundle-Classpath does not have .jar extension: "
+                                        + jarOrDir);
+                            list.add(sub);
+                        } catch (Exception e) {
+                            error("Invalid embedded JAR file on Bundle-Classpath: "
+                                    + jarOrDir + ", " + e);
+                        }
+                    } else if (dot.getDirectories().containsKey(jarOrDir)) {
+                        if (r3)
+                            error("R3 bundles do not support directories on the Bundle-ClassPath: "
+                                    + jarOrDir);
+
+                        try {
+                            Jar sub = new Jar(jarOrDir);
+                            addClose(sub);
+                            for (Map.Entry<String, Resource> entry : dot
+                                    .getResources().entrySet()) {
+                                if (entry.getKey().startsWith(jarOrDir))
+                                    sub.putResource(entry.getKey().substring(
+                                            jarOrDir.length() + 1), entry
+                                            .getValue());
+                            }
+                            list.add(sub);
+                        } catch (Exception e) {
+                            error("Invalid embedded directory file on Bundle-Classpath: "
+                                    + jarOrDir + ", " + e);
+                        }
+                    } else {
+                        error("Cannot find a file or directory for Bundle-Classpath entry: "
+                                + jarOrDir);
+                    }
+                }
+            }
+        }
+        return list;
+    }
+
+    /*
+     * Bundle-NativeCode ::= nativecode ( ',' nativecode )* ( ’,’ optional) ?
+     * nativecode ::= path ( ';' path )* // See 1.4.2 ( ';' parameter )+
+     * optional ::= ’*’
+     */
+    public void verifyNative() {
+        String nc = getHeader("Bundle-NativeCode");
+        doNative(nc);
+    }
+
+    public void doNative(String nc) {
+        if (nc != null) {
+            QuotedTokenizer qt = new QuotedTokenizer(nc, ",;=", false);
+            char del;
+            do {
+                do {
+                    String name = qt.nextToken();
+                    if (name == null) {
+                        error("Can not parse name from bundle native code header: "
+                                + nc);
+                        return;
+                    }
+                    del = qt.getSeparator();
+                    if (del == ';') {
+                        if (dot != null && !dot.exists(name)) {
+                            error("Native library not found in JAR: " + name);
+                        }
+                    } else {
+                        String value = null;
+                        if (del == '=')
+                            value = qt.nextToken();
+
+                        String key = name.toLowerCase();
+                        if (key.equals("osname")) {
+                            // ...
+                        } else if (key.equals("osversion")) {
+                            // verify version range
+                            verify(value, VERSIONRANGE);
+                        } else if (key.equals("language")) {
+                            verify(value, ISO639);
+                        } else if (key.equals("processor")) {
+                            // verify(value, PROCESSORS);
+                        } else if (key.equals("selection-filter")) {
+                            // verify syntax filter
+                            verifyFilter(value);
+                        } else if (name.equals("*") && value == null) {
+                            // Wildcard must be at end.
+                            if (qt.nextToken() != null)
+                                error("Bundle-Native code header may only END in wildcard: nc");
+                        } else {
+                            warning("Unknown attribute in native code: " + name
+                                    + "=" + value);
+                        }
+                        del = qt.getSeparator();
+                    }
+                } while (del == ';');
+            } while (del == ',');
+        }
+    }
+
+    public void verifyFilter(String value) {
+        try {
+            verifyFilter(value, 0);
+        } catch (Exception e) {
+            error("Not a valid filter: " + value + e.getMessage());
+        }
+    }
+
+    private void verifyActivator() {
+        String bactivator = getHeader("Bundle-Activator");
+        if (bactivator != null) {
+            Clazz cl = loadClass(bactivator);
+            if (cl == null) {
+                int n = bactivator.lastIndexOf('.');
+                if (n > 0) {
+                    String pack = bactivator.substring(0, n);
+                    if (mimports.containsKey(pack))
+                        return;
+                    error("Bundle-Activator not found on the bundle class path nor in imports: "
+                            + bactivator);
+                } else
+                    error("Activator uses default package and is not local (default package can not be imported): "
+                            + bactivator);
+            }
+        }
+    }
+
+    private Clazz loadClass(String className) {
+        String path = className.replace('.', '/') + ".class";
+        return (Clazz) classSpace.get(path);
+    }
+
+    private void verifyComponent() {
+        String serviceComponent = getHeader("Service-Component");
+        if (serviceComponent != null) {
+            Map<String, Map<String, String>> map = parseHeader(serviceComponent);
+            for (String component : map.keySet()) {
+                if (component.indexOf("*") < 0 && !dot.exists(component)) {
+                    error("Service-Component entry can not be located in JAR: "
+                            + component);
+                } else {
+                    // validate component ...
+                }
+            }
+        }
+    }
+
+    public void info() {
+        System.out.println("Refers                           : " + referred);
+        System.out.println("Contains                         : " + contained);
+        System.out.println("Manifest Imports                 : " + mimports);
+        System.out.println("Manifest Exports                 : " + mexports);
+    }
+
+    /**
+     * Invalid exports are exports mentioned in the manifest but not found on
+     * the classpath. This can be calculated with: exports - contains.
+     * 
+     * Unfortunately, we also must take duplicate names into account. These
+     * duplicates are of course no erroneous.
+     */
+    private void verifyInvalidExports() {
+        Set<String> invalidExport = newSet(mexports.keySet());
+        invalidExport.removeAll(contained.keySet());
+
+        // We might have duplicate names that are marked for it. These
+        // should not be counted. Should we test them against the contained
+        // set? Hmm. If someone wants to hang himself by using duplicates than
+        // I guess he can go ahead ... This is not a recommended practice
+        for (Iterator<String> i = invalidExport.iterator(); i.hasNext();) {
+            String pack = i.next();
+            if (isDuplicate(pack))
+                i.remove();
+        }
+
+        if (!invalidExport.isEmpty())
+            error("Exporting packages that are not on the Bundle-Classpath"
+                    + bundleClasspath + ": " + invalidExport);
+    }
+
+    /**
+     * Invalid imports are imports that we never refer to. They can be
+     * calculated by removing the refered packages from the imported packages.
+     * This leaves packages that the manifest imported but that we never use.
+     */
+    private void verifyInvalidImports() {
+        Set<String> invalidImport = newSet(mimports.keySet());
+        invalidImport.removeAll(referred.keySet());
+        // TODO Added this line but not sure why it worked before ...
+        invalidImport.removeAll(contained.keySet());
+        String bactivator = getHeader(Analyzer.BUNDLE_ACTIVATOR);
+        if (bactivator != null) {
+            int n = bactivator.lastIndexOf('.');
+            if (n > 0) {
+                invalidImport.remove(bactivator.substring(0, n));
+            }
+        }
+        if (isPedantic() && !invalidImport.isEmpty())
+            warning("Importing packages that are never refered to by any class on the Bundle-Classpath"
+                    + bundleClasspath + ": " + invalidImport);
+    }
+
+    /**
+     * Check for unresolved imports. These are referals that are not imported by
+     * the manifest and that are not part of our bundle classpath. The are
+     * calculated by removing all the imported packages and contained from the
+     * refered packages.
+     */
+    private void verifyUnresolvedReferences() {
+        Set<String> unresolvedReferences = new TreeSet<String>(referred
+                .keySet());
+        unresolvedReferences.removeAll(mimports.keySet());
+        unresolvedReferences.removeAll(contained.keySet());
+
+        // Remove any java.** packages.
+        for (Iterator<String> p = unresolvedReferences.iterator(); p.hasNext();) {
+            String pack = p.next();
+            if (pack.startsWith("java.") || ignore.containsKey(pack))
+                p.remove();
+            else {
+                // Remove any dynamic imports
+                if (isDynamicImport(pack))
+                    p.remove();
+            }
+        }
+
+        if (!unresolvedReferences.isEmpty()) {
+            // Now we want to know the
+            // classes that are the culprits
+            Set<String> culprits = new HashSet<String>();
+            for (Clazz clazz : classSpace.values()) {
+                if (hasOverlap(unresolvedReferences, clazz.imports.keySet()))
+                    culprits.add(clazz.getPath());
+            }
+
+            error("Unresolved references to " + unresolvedReferences
+                    + " by class(es) on the Bundle-Classpath" + bundleClasspath
+                    + ": " + culprits);
+        }
+    }
+
+    /**
+     * @param p
+     * @param pack
+     */
+    private boolean isDynamicImport(String pack) {
+        for (String pattern : mdynimports.keySet()) {
+            // Wildcard?
+            if (pattern.equals("*"))
+                return true; // All packages can be dynamically imported
+
+            if (pattern.endsWith(".*")) {
+                pattern = pattern.substring(0, pattern.length() - 2);
+                if (pack.startsWith(pattern)
+                        && (pack.length() == pattern.length() || pack
+                                .charAt(pattern.length()) == '.'))
+                    return true;
+            } else {
+                if (pack.equals(pattern))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasOverlap(Set<?> a, Set<?> b) {
+        for (Iterator<?> i = a.iterator(); i.hasNext();) {
+            if (b.contains(i.next()))
+                return true;
+        }
+        return false;
+    }
+
+    public void verify() throws IOException {
+        if (classSpace == null)
+            classSpace = analyzeBundleClasspath(dot,
+                    parseHeader(getHeader(Analyzer.BUNDLE_CLASSPATH)),
+                    contained, referred, uses);
+        verifyManifestFirst();
+        verifyActivator();
+        verifyActivationPolicy();
+        verifyComponent();
+        verifyNative();
+        verifyInvalidExports();
+        verifyInvalidImports();
+        verifyUnresolvedReferences();
+        verifySymbolicName();
+        verifyListHeader("Bundle-RequiredExecutionEnvironment", EENAME, false);
+        verifyHeader("Bundle-ManifestVersion", BUNDLEMANIFESTVERSION, false);
+        verifyHeader("Bundle-Version", VERSION, true);
+        verifyListHeader("Bundle-Classpath", FILE, false);
+        verifyDynamicImportPackage();
+        verifyBundleClasspath();
+        if (usesRequire) {
+            if (!getErrors().isEmpty()) {
+                getWarnings()
+                        .add(
+                                0,
+                                "Bundle uses Require Bundle, this can generate false errors because then not enough information is available without the required bundles");
+            }
+        }
+    }
+
+    public boolean verifyActivationPolicy() {
+        String policy = getHeader(Constants.BUNDLE_ACTIVATIONPOLICY);
+        if (policy == null)
+            return true;
+
+        return verifyActivationPolicy(policy);
+    }
+
+    public boolean verifyActivationPolicy(String policy) {
+        Map<String, Map<String, String>> map = parseHeader(policy);
+        if (map.size() == 0)
+            warning("Bundle-ActivationPolicy is set but has no argument %s",
+                    policy);
+        else if (map.size() > 1)
+            warning("Bundle-ActivationPolicy has too many arguments %s", policy);
+        else {
+            Map<String, String> s = map.get("lazy");
+            if (s == null)
+                warning(
+                        "Bundle-ActivationPolicy set but is not set to lazy: %s",
+                        policy);
+            else
+                return true;
+        }
+
+        return false;
+    }
+
+    public void verifyBundleClasspath() {
+        Map<String, Map<String, String>> bcp = parseHeader(getHeader(Analyzer.BUNDLE_CLASSPATH));
+        if (bcp.isEmpty() || bcp.containsKey("."))
+            return;
+
+        for (String path : dot.getResources().keySet()) {
+            if (path.endsWith(".class")) {
+                warning("The Bundle-Classpath does not contain the actual bundle JAR (as specified with '.' in the Bundle-Classpath) but the JAR does contain classes. Is this intentional?");
+                return;
+            }
+        }
+    }
+
+    /**
+     * <pre>
+     *          DynamicImport-Package ::= dynamic-description
+     *              ( ',' dynamic-description )*
+     *              
+     *          dynamic-description::= wildcard-names ( ';' parameter )*
+     *          wildcard-names ::= wildcard-name ( ';' wildcard-name )*
+     *          wildcard-name ::= package-name 
+     *                         | ( package-name '.*' ) // See 1.4.2
+     *                         | '*'
+     * </pre>
+     */
+    private void verifyDynamicImportPackage() {
+        verifyListHeader("DynamicImport-Package", WILDCARDPACKAGE, true);
+        String dynamicImportPackage = getHeader("DynamicImport-Package");
+        if (dynamicImportPackage == null)
+            return;
+
+        Map<String, Map<String, String>> map = parseHeader(dynamicImportPackage);
+        for (String name : map.keySet()) {
+            name = name.trim();
+            if (!verify(name, WILDCARDPACKAGE))
+                error("DynamicImport-Package header contains an invalid package name: "
+                        + name);
+
+            Map<String, String> sub = map.get(name);
+            if (r3 && sub.size() != 0) {
+                error("DynamicPackage-Import has attributes on import: "
+                        + name
+                        + ". This is however, an <=R3 bundle and attributes on this header were introduced in R4. ");
+            }
+        }
+    }
+
+    private void verifyManifestFirst() {
+        if (!dot.manifestFirst) {
+            error("Invalid JAR stream: Manifest should come first to be compatible with JarInputStream, it was not");
+        }
+    }
+
+    private void verifySymbolicName() {
+        Map<String, Map<String, String>> bsn = parseHeader(getHeader(Analyzer.BUNDLE_SYMBOLICNAME));
+        if (!bsn.isEmpty()) {
+            if (bsn.size() > 1)
+                error("More than one BSN specified " + bsn);
+
+            String name = (String) bsn.keySet().iterator().next();
+            if (!SYMBOLICNAME.matcher(name).matches()) {
+                error("Symbolic Name has invalid format: " + name);
+            }
+        }
+    }
+
+    /**
+     * <pre>
+     *         filter ::= ’(’ filter-comp ’)’
+     *         filter-comp ::= and | or | not | operation
+     *         and ::= ’&amp;’ filter-list
+     *         or ::= ’|’ filter-list
+     *         not ::= ’!’ filter
+     *         filter-list ::= filter | filter filter-list
+     *         operation ::= simple | present | substring
+     *         simple ::= attr filter-type value
+     *         filter-type ::= equal | approx | greater | less
+     *         equal ::= ’=’
+     *         approx ::= ’&tilde;=’
+     *         greater ::= ’&gt;=’
+     *         less ::= ’&lt;=’
+     *         present ::= attr ’=*’
+     *         substring ::= attr ’=’ initial any final
+     *         inital ::= () | value
+     *         any ::= ’*’ star-value
+     *         star-value ::= () | value ’*’ star-value
+     *         final ::= () | value
+     *         value ::= &lt;see text&gt;
+     * </pre>
+     * 
+     * @param expr
+     * @param index
+     * @return
+     */
+
+    int verifyFilter(String expr, int index) {
+        try {
+            while (Character.isWhitespace(expr.charAt(index)))
+                index++;
+
+            if (expr.charAt(index) != '(')
+                throw new IllegalArgumentException(
+                        "Filter mismatch: expected ( at position " + index
+                                + " : " + expr);
+
+            index++;
+            while (Character.isWhitespace(expr.charAt(index)))
+                index++;
+
+            switch (expr.charAt(index)) {
+            case '!':
+            case '&':
+            case '|':
+                return verifyFilterSubExpression(expr, index) + 1;
+
+            default:
+                return verifyFilterOperation(expr, index) + 1;
+            }
+        } catch (IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException(
+                    "Filter mismatch: early EOF from " + index);
+        }
+    }
+
+    private int verifyFilterOperation(String expr, int index) {
+        StringBuffer sb = new StringBuffer();
+        while ("=><~()".indexOf(expr.charAt(index)) < 0) {
+            sb.append(expr.charAt(index++));
+        }
+        String attr = sb.toString().trim();
+        if (attr.length() == 0)
+            throw new IllegalArgumentException(
+                    "Filter mismatch: attr at index " + index + " is 0");
+        sb = new StringBuffer();
+        while ("=><~".indexOf(expr.charAt(index)) >= 0) {
+            sb.append(expr.charAt(index++));
+        }
+        String operator = sb.toString();
+        if (!verify(operator, FILTEROP))
+            throw new IllegalArgumentException(
+                    "Filter error, illegal operator " + operator + " at index "
+                            + index);
+
+        sb = new StringBuffer();
+        while (")".indexOf(expr.charAt(index)) < 0) {
+            switch (expr.charAt(index)) {
+            case '\\':
+                if (expr.charAt(index + 1) == '*'
+                        || expr.charAt(index + 1) == ')')
+                    index++;
+                else
+                    throw new IllegalArgumentException(
+                            "Filter error, illegal use of backslash at index "
+                                    + index
+                                    + ". Backslash may only be used before * or (");
+            }
+            sb.append(expr.charAt(index++));
+        }
+        return index;
+    }
+
+    private int verifyFilterSubExpression(String expr, int index) {
+        do {
+            index = verifyFilter(expr, index + 1);
+            while (Character.isWhitespace(expr.charAt(index)))
+                index++;
+            if (expr.charAt(index) != ')')
+                throw new IllegalArgumentException(
+                        "Filter mismatch: expected ) at position " + index
+                                + " : " + expr);
+            index++;
+        } while (expr.charAt(index) == '(');
+        return index;
+    }
+
+    private String getHeader(String string) {
+        return main.getValue(string);
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean verifyHeader(String name, Pattern regex, boolean error) {
+        String value = manifest.getMainAttributes().getValue(name);
+        if (value == null)
+            return false;
+
+        QuotedTokenizer st = new QuotedTokenizer(value.trim(), ",");
+        for (Iterator<String> i = st.getTokenSet().iterator(); i.hasNext();) {
+            if (!verify((String) i.next(), regex)) {
+                String msg = "Invalid value for " + name + ", " + value
+                        + " does not match " + regex.pattern();
+                if (error)
+                    error(msg);
+                else
+                    warning(msg);
+            }
+        }
+        return true;
+    }
+
+    private boolean verify(String value, Pattern regex) {
+        return regex.matcher(value).matches();
+    }
+
+    private boolean verifyListHeader(String name, Pattern regex, boolean error) {
+        String value = manifest.getMainAttributes().getValue(name);
+        if (value == null)
+            return false;
+
+        Map<String, Map<String, String>> map = parseHeader(value);
+        for (String header : map.keySet()) {
+            if (!regex.matcher(header).matches()) {
+                String msg = "Invalid value for " + name + ", " + value
+                        + " does not match " + regex.pattern();
+                if (error)
+                    error(msg);
+                else
+                    warning(msg);
+            }
+        }
+        return true;
+    }
+
+    public String getProperty(String key, String deflt) {
+        if (properties == null)
+            return deflt;
+        return properties.getProperty(key, deflt);
+    }
+
+    public void setClassSpace(Map<String, Clazz> classspace,
+            Map<String, Map<String, String>> contained,
+            Map<String, Map<String, String>> referred,
+            Map<String, Set<String>> uses) {
+        this.classSpace = classspace;
+        this.contained = contained;
+        this.referred = referred;
+        this.uses = uses;
+    }
+
+    public static boolean isVersion(String version) {
+        return VERSION.matcher(version).matches();
+    }
+
+    public static boolean isIdentifier(String value) {
+        if (value.length() < 1)
+            return false;
+
+        if (!Character.isJavaIdentifierStart(value.charAt(0)))
+            return false;
+
+        for (int i = 1; i < value.length(); i++) {
+            if (!Character.isJavaIdentifierPart(value.charAt(i)))
+                return false;
+        }
+        return true;
+    }
+
+    public static boolean isMember(String value, String[] matches) {
+        for (String match : matches) {
+            if (match.equals(value))
+                return true;
+        }
+        return false;
+    }
+
+    /*
+    public int verifyFilter(StringBuffer sb, String s, int rover) {
+        rover = skip(s, rover);
+        char c = s.charAt(rover);
+        if (c == '(') {
+            sb.append('(');
+            char type;
+            rover = skip(s, ++rover);
+            c = s.charAt(rover);
+            switch (c) {
+            case '!': // not
+            case '&': // and
+            case '|': // or
+                sb.append(c);
+                type = c;
+                while(true) {
+                    rover = skip(++rover);
+                    c = s.charAt(rover);
+                    if ( c != '(')
+                        break;
+                    rover = verifyFilter(s, rover);
+                }
+                break;
+
+            case ')':
+                return rover + 1;
+
+            default:
+                rover = skip(s,rover);
+                c = s.charAt(rover);
+                while ( Character.isLetterOrDigit(c) || )
+            }
+        }
+    }
+    */
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/WriteResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/WriteResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/WriteResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,41 @@
+package aQute.lib.osgi;
+
+import java.io.*;
+
+public abstract class WriteResource implements Resource {
+	long 	lastModified;
+	String	extra;
+
+	public InputStream openInputStream() throws IOException {
+	    PipedInputStream pin = new PipedInputStream();
+	    final PipedOutputStream pout = new PipedOutputStream(pin);
+	    Thread t = new Thread() {
+	        public void run() {
+	            try {
+                    write(pout);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    try {
+                        pout.close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+	        }
+	    };
+	    return pin;
+	}
+
+	public abstract void write(OutputStream out) throws IOException;
+	
+	public abstract long lastModified();
+
+	public String getExtra() {
+		return extra;
+	}
+
+	public void setExtra(String extra) {
+		this.extra = extra;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ZipResource.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ZipResource.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/ZipResource.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,80 @@
+package aQute.lib.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) {
+        this.zip = zip;
+        this.entry = entry;
+        this.lastModified = lastModified;
+        byte[] data = entry.getExtra();
+        if (data != null)
+            this.extra = new String(data);
+    }
+
+    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 (FileNotFoundException e) {
+            throw new IllegalArgumentException("Problem opening JAR: "
+                    + file.getAbsolutePath());
+        }
+    }
+
+    public void write(OutputStream out) throws IOException {
+        FileResource.copy(this, out);
+    }
+
+    public long lastModified() {
+        return lastModified;
+    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/osgi/eclipse/EclipseClasspath.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,248 @@
+package aQute.lib.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.libg.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: 1.2 $
+ */
+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,
+            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 = (String) properties.get(var);
+            if (base != null) {
+                File b = new File(base);
+                File f = new File(b, remainder.replace('/', File.separatorChar));
+                return f;
+            } else
+                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;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/tag/Tag.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/tag/Tag.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/lib/tag/Tag.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,457 @@
+package aQute.lib.tag;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+/**
+ * The Tag class represents a minimal XML tree. It consist of a named element
+ * with a hashtable of named attributes. Methods are provided to walk the tree
+ * and get its constituents. The content of a Tag is a list that contains String
+ * objects or other Tag objects.
+ */
+public class Tag {
+    Tag                       parent;                               // Parent
+    // element
+    String                    name;                                 // Name
+    // of
+    // the
+    // tag
+    final Map<String, String> attributes;
+    // name
+    // ->
+    // value
+    final List<Object>        content = new ArrayList<Object>();    // Content
+    // elements
+
+    static SimpleDateFormat   format  = new SimpleDateFormat(
+                                              "yyyyMMddhhmmss.SSS");
+
+    /**
+     * Construct a new Tag with a name.
+     */
+    public Tag(String name) {
+        this.name = name;
+        attributes = new LinkedHashMap<String, String>(); // Attributes
+    }
+
+    /**
+     * Construct a new Tag with a name.
+     */
+    public Tag(String name, Map<String, String> attributes) {
+        this.name = name;
+        this.attributes = attributes;
+    }
+
+    /**
+     * Construct a new Tag with a name and a set of attributes. The attributes
+     * are given as ( name, value ) ...
+     */
+    public Tag(String name, String[] attributes) {
+        this.name = name;
+        this.attributes = new LinkedHashMap<String, String>(); // Attributes
+        for (int i = 0; i < attributes.length; i += 2)
+            addAttribute(attributes[i], attributes[i + 1]);
+    }
+
+    /**
+     * Construct a new Tag with a single string as content.
+     */
+    public Tag(String name, String content) {
+        this.name = name;
+        attributes = new LinkedHashMap<String, String>(); // Attributes
+        addContent(content);
+    }
+
+    /**
+     * Add a new attribute.
+     */
+    public void addAttribute(String key, String value) {
+        attributes.put(key, value);
+    }
+
+    /**
+     * Add a new attribute.
+     */
+    public void addAttribute(String key, Object value) {
+        if (value == null)
+            return;
+        attributes.put(key, value.toString());
+    }
+
+    /**
+     * Add a new attribute.
+     */
+    public void addAttribute(String key, int value) {
+        attributes.put(key, Integer.toString(value));
+    }
+
+    /**
+     * Add a new date attribute. The date is formatted as the SimpleDateFormat
+     * describes at the top of this class.
+     */
+    public void addAttribute(String key, Date value) {
+        attributes.put(key, format.format(value));
+    }
+
+    /**
+     * Add a new content string.
+     */
+    public void addContent(String string) {
+        content.add(string);
+    }
+
+    /**
+     * Add a new content tag.
+     */
+    public void addContent(Tag tag) {
+        content.add(tag);
+        tag.parent = this;
+    }
+
+    /**
+     * Return the name of the tag.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Return the attribute value.
+     */
+    public String getAttribute(String key) {
+        return (String) attributes.get(key);
+    }
+
+    /**
+     * Return the attribute value or a default if not defined.
+     */
+    public String getAttribute(String key, String deflt) {
+        String answer = getAttribute(key);
+        return answer == null ? deflt : answer;
+    }
+
+    /**
+     * Answer the attributes as a Dictionary object.
+     */
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Return the contents.
+     */
+    public List<Object> getContents() {
+        return content;
+    }
+
+    /**
+     * Return a string representation of this Tag and all its children
+     * recursively.
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        print(0, new PrintWriter(sw));
+        return sw.toString();
+    }
+
+    /**
+     * Return only the tags of the first level of descendants that match the
+     * name.
+     */
+    public List<Object> getContents(String tag) {
+        List<Object> out = new ArrayList<Object>();
+        for (Object o : out) {
+            if (o instanceof Tag && ((Tag) o).getName().equals(tag))
+                out.add(o);
+        }
+        return out;
+    }
+
+    /**
+     * Return the whole contents as a String (no tag info and attributes).
+     */
+    public String getContentsAsString() {
+        StringBuffer sb = new StringBuffer();
+        getContentsAsString(sb);
+        return sb.toString();
+    }
+
+    /**
+     * convenient method to get the contents in a StringBuffer.
+     */
+    public void getContentsAsString(StringBuffer sb) {
+        for (Object o : content) {
+            if (o instanceof Tag)
+                ((Tag) o).getContentsAsString(sb);
+            else
+                sb.append(o.toString());
+        }
+    }
+
+    /**
+     * Print the tag formatted to a PrintWriter.
+     */
+    public void print(int indent, PrintWriter pw) {
+        pw.print("\n");
+        spaces(pw, indent);
+        pw.print('<');
+        pw.print(name);
+
+        for (String key : attributes.keySet()) {
+            String value = escape(attributes.get(key));
+            pw.print(' ');
+            pw.print(key);
+            pw.print("=");
+            String quote = "'";
+            if (value.indexOf(quote) >= 0)
+                quote = "\"";
+            pw.print(quote);
+            pw.print(value);
+            pw.print(quote);
+        }
+
+        if (content.size() == 0)
+            pw.print('/');
+        else {
+            pw.print('>');
+            for (Object c : content) {
+                if (c instanceof String) {
+                    formatted(pw, indent + 2, 60, escape((String) c));
+                } else if (c instanceof Tag) {
+                    Tag tag = (Tag) c;
+                    tag.print(indent + 2, pw);
+                }
+            }
+            pw.print("\n");
+            spaces(pw, indent);
+            pw.print("</");
+            pw.print(name);
+        }
+        pw.print('>');
+    }
+
+    /**
+     * Convenience method to print a string nicely and does character conversion
+     * to entities.
+     */
+    void formatted(PrintWriter pw, int left, int width, String s) {
+        int pos = width + 1;
+        s = s.trim();
+
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (i == 0 || (Character.isWhitespace(c) && pos > width - 3)) {
+                pw.print("\n");
+                spaces(pw, left);
+                pos = 0;
+            }
+            switch (c) {
+            case '<':
+                pw.print("&lt;");
+                pos += 4;
+                break;
+            case '>':
+                pw.print("&gt;");
+                pos += 4;
+                break;
+            case '&':
+                pw.print("&amp;");
+                pos += 5;
+                break;
+            default:
+                pw.print(c);
+                pos++;
+                break;
+            }
+
+        }
+    }
+
+    /**
+     * Escape a string, do entity conversion.
+     */
+    String escape(String s) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            switch (c) {
+            case '<':
+                sb.append("&lt;");
+                break;
+            case '>':
+                sb.append("&gt;");
+                break;
+            case '&':
+                sb.append("&amp;");
+                break;
+            default:
+                sb.append(c);
+                break;
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Make spaces.
+     */
+    void spaces(PrintWriter pw, int n) {
+        while (n-- > 0)
+            pw.print(' ');
+    }
+
+    /**
+     * root/preferences/native/os
+     */
+    public Collection<Tag> select(String path) {
+        return select(path, (Tag) null);
+    }
+
+    public Collection<Tag> select(String path, Tag mapping) {
+        List<Tag> v = new ArrayList<Tag>();
+        select(path, v, mapping);
+        return v;
+    }
+
+    void select(String path, List<Tag> results, Tag mapping) {
+        if (path.startsWith("//")) {
+            int i = path.indexOf('/', 2);
+            String name = path.substring(2, i < 0 ? path.length() : i);
+
+            for (Object o : content) {
+                if (o instanceof Tag) {
+                    Tag child = (Tag) o;
+                    if (match(name, child, mapping))
+                        results.add(child);
+                    child.select(path, results, mapping);
+                }
+
+            }
+            return;
+        }
+
+        if (path.length() == 0) {
+            results.add(this);
+            return;
+        }
+
+        int i = path.indexOf("/");
+        String elementName = path;
+        String remainder = "";
+        if (i > 0) {
+            elementName = path.substring(0, i);
+            remainder = path.substring(i + 1);
+        }
+
+        for (Object o : content) {
+            if (o instanceof Tag) {
+                Tag child = (Tag) o;
+                if (child.getName().equals(elementName)
+                        || elementName.equals("*"))
+                    child.select(remainder, results, mapping);
+            }
+        }
+    }
+
+    public boolean match(String search, Tag child, Tag mapping) {
+        String target = child.getName();
+        String sn = null;
+        String tn = null;
+
+        if (search.equals("*"))
+            return true;
+
+        int s = search.indexOf(':');
+        if (s > 0) {
+            sn = search.substring(0, s);
+            search = search.substring(s + 1);
+        }
+        int t = target.indexOf(':');
+        if (t > 0) {
+            tn = target.substring(0, t);
+            target = target.substring(t + 1);
+        }
+
+        if (!search.equals(target)) // different tag names
+            return false;
+
+        if (mapping == null) {
+            return tn == sn || (sn != null && sn.equals(tn));
+        } else {
+            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));
+        }
+    }
+
+    public String getString(String path) {
+        String attribute = null;
+        int index = path.indexOf("@");
+        if (index >= 0) {
+            // attribute
+            attribute = path.substring(index + 1);
+
+            if (index > 0) {
+                // prefix path
+                path = path.substring(index - 1); // skip -1
+            } else
+                path = "";
+        }
+        Collection<Tag> tags = select(path);
+        StringBuffer sb = new StringBuffer();
+        for (Tag tag : tags) {
+            if (attribute == null)
+                tag.getContentsAsString(sb);
+            else
+                sb.append(tag.getAttribute(attribute));
+        }
+        return sb.toString();
+    }
+
+    public String getStringContent() {
+        StringBuffer sb = new StringBuffer();
+        for (Object c : content) {
+            if (!(c instanceof Tag))
+                sb.append(c);
+        }
+        return sb.toString();
+    }
+
+    public String getNameSpace() {
+        return getNameSpace(name);
+    }
+
+    public String getNameSpace(String name) {
+        int index = name.indexOf(':');
+        if (index > 0) {
+            String ns = name.substring(0, index);
+            return findRecursiveAttribute("xmlns:" + ns);
+        } else
+            return findRecursiveAttribute("xmlns");
+    }
+
+    public String findRecursiveAttribute(String name) {
+        String value = getAttribute(name);
+        if (value != null)
+            return value;
+        if (parent != null)
+            return parent.findRecursiveAttribute(name);
+        return null;
+    }
+
+    public String getLocalName() {
+        int index = name.indexOf(':');
+        if (index <= 0)
+            return name;
+
+        return name.substring(index + 1);
+    }
+
+    public void rename(String string) {
+        name = string;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/generics/Create.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/generics/Create.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/generics/Create.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,40 @@
+package aQute.libg.generics;
+
+import java.util.*;
+
+public class Create {
+    
+    public static <K,V>  Map<K, V> map() {
+        return new LinkedHashMap<K,V>();
+    }
+
+    public static <T>  List<T> list() {
+        return new ArrayList<T>();
+    }
+
+    public static <T>  Set<T> set() {
+        return new HashSet<T>();
+    }
+
+    public static <T>  List<T> list(T[] source) {
+        return new ArrayList<T>(Arrays.asList(source));
+    }
+
+    public static <T>  Set<T> set(T[]source) {
+        return new HashSet<T>(Arrays.asList(source));
+    }
+
+    public static <K,V>  Map<K, V> copy(Map<K,V> source) {
+        return new LinkedHashMap<K,V>(source);
+    }
+
+    public static <T>  List<T> copy(List<T> source) {
+        return new ArrayList<T>(source);
+    }
+
+    public static <T>  Set<T> copy(Set<T> source) {
+        return new HashSet<T>(source);
+    }
+
+    
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/header/OSGiHeader.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/header/OSGiHeader.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/header/OSGiHeader.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,145 @@
+package aQute.libg.header;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+import aQute.libg.qtokens.*;
+import aQute.libg.reporter.*;
+
+public class OSGiHeader {
+
+    static public Map<String, Map<String, String>> parseHeader(String value) {
+        return parseHeader(value, null);
+    }
+
+    /**
+     * Standard OSGi header parser. This parser can handle the format clauses
+     * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
+     * value )
+     * 
+     * This is mapped to a Map { name => Map { attr|directive => value } }
+     * 
+     * @param value
+     *            A string
+     * @return a Map<String,Map<String,String>>
+     */
+    static public Map<String, Map<String, String>> parseHeader(String value,
+            Reporter logger) {
+        if (value == null || value.trim().length() == 0)
+            return Create.map();
+
+        Map<String, Map<String, String>> result = Create.map();
+        QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+        char del = 0;
+        do {
+            boolean hadAttribute = false;
+            Map<String, String> clause = Create.map();
+            List<String> aliases = Create.list();
+            String name = qt.nextToken(",;");
+
+            del = qt.getSeparator();
+            if (name == null || name.length() == 0) {
+                if (logger != null && logger.isPedantic()) {
+                    logger
+                            .warning("Empty clause, usually caused by repeating a comma without any name field or by having spaces after the backslash of a property file: "
+                                    + value);
+                }
+                if (name == null)
+                    break;
+            } else {
+                name = name.trim();
+
+                aliases.add(name);
+                while (del == ';') {
+                    String adname = qt.nextToken();
+                    if ((del = qt.getSeparator()) != '=') {
+                        if (hadAttribute)
+                            if (logger != null) {
+                                logger
+                                        .error("Header contains name field after attribute or directive: "
+                                                + adname
+                                                + " from "
+                                                + value
+                                                + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4");
+                            }
+                        if (adname != null && adname.length() > 0)
+                            aliases.add(adname.trim());
+                    } else {
+                        String advalue = qt.nextToken();
+                        if (clause.containsKey(adname)) {
+                            if (logger != null && logger.isPedantic())
+                                logger
+                                        .warning("Duplicate attribute/directive name "
+                                                + adname
+                                                + " in "
+                                                + value
+                                                + ". This attribute/directive will be ignored");
+                        }
+                        if (advalue == null) {
+                            if (logger != null)
+                                logger
+                                        .error("No value after '=' sign for attribute "
+                                                + adname);
+                            advalue = "";
+                        }
+                        clause.put(adname.trim(), advalue.trim());
+                        del = qt.getSeparator();
+                        hadAttribute = true;
+                    }
+                }
+
+                // Check for duplicate names. The aliases list contains
+                // the list of nams, for each check if it exists. If so,
+                // add a number of "~" to make it unique.
+                for (String clauseName : aliases) {
+                    if (result.containsKey(clauseName)) {
+                        if (logger != null && logger.isPedantic())
+                            logger
+                                    .warning("Duplicate name "
+                                            + clauseName
+                                            + " used in header: '"
+                                            + clauseName
+                                            + "'. Duplicate names are specially marked in Bnd with a ~ at the end (which is stripped at printing time).");
+                        while (result.containsKey(clauseName))
+                            clauseName += "~";
+                    }
+                    result.put(clauseName, clause);
+                }
+            }
+        } while (del == ',');
+        return result;
+    }
+
+    public static Map<String, String> parseProperties(String input) {
+        return parseProperties(input, null);
+    }
+
+    public static Map<String, String> parseProperties(String input, Reporter logger) {
+        if (input == null || input.trim().length() == 0)
+            return Create.map();
+
+        Map<String, String> result = Create.map();
+        QuotedTokenizer qt = new QuotedTokenizer(input, "=,");
+        char del = ',';
+
+        while (del == ',') {
+            String key = qt.nextToken(",=");
+            String value = "";
+            del = qt.getSeparator();
+            if (del == '=') {
+                value = qt.nextToken(",=");
+                del = qt.getSeparator();
+            }
+            result.put(key, value);
+        }
+        if (del != 0)
+            if ( logger == null )
+            throw new IllegalArgumentException(
+                    "Invalid syntax for properties: " + input);
+            else
+                logger.error("Invalid syntax for properties: " + input);
+
+        return result;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/qtokens/QuotedTokenizer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,118 @@
+package aQute.libg.qtokens;
+
+import java.util.*;
+
+import aQute.libg.generics.*;
+
+public class QuotedTokenizer {
+	String	string;
+	int		index				= 0;
+	String	separators;
+	boolean	returnTokens;
+	boolean	ignoreWhiteSpace	= true;
+	String	peek;
+	char	separator;
+
+	public QuotedTokenizer(String string, String separators, boolean returnTokens ) {
+		if ( string == null )
+			throw new IllegalArgumentException("string argument must be not null");
+		this.string = string;
+		this.separators = separators;
+		this.returnTokens = returnTokens;
+	}
+	public QuotedTokenizer(String string, String separators) {
+		this(string,separators,false);
+	}
+
+	public String nextToken(String separators) {
+		separator = 0;
+		if ( peek != null ) {
+			String tmp = peek;
+			peek = null;
+			return tmp;
+		}
+		
+		if ( index == string.length())
+			return null;
+		
+		StringBuffer sb = new StringBuffer();
+
+		while (index < string.length()) {
+			char c = string.charAt(index++);
+
+			if ( Character.isWhitespace(c)) {
+				if ( index == string.length())
+					break;
+				else {
+				    sb.append(c);
+					continue;
+				}
+			}
+			
+			if (separators.indexOf(c) >= 0) {
+				if (returnTokens)
+					peek = Character.toString(c);
+				else
+					separator = c;
+				break;
+			}
+
+			switch (c) {
+				case '"' :
+				case '\'' :
+					quotedString(sb, c);
+					break;
+
+				default :
+					sb.append(c);
+			}
+		}
+		String result = sb.toString().trim();
+		if ( result.length()==0 && index==string.length())
+			return null;
+		return result;
+	}
+
+	public String nextToken() {
+		return nextToken(separators);
+	}
+
+	private void quotedString(StringBuffer sb, char c) {
+		char quote = c;
+		while (index < string.length()) {
+			c = string.charAt(index++);
+			if (c == quote)
+				break;
+			if (c == '\\' && index < string.length()
+					&& string.charAt(index + 1) == quote)
+				c = string.charAt(index++);
+			sb.append(c);
+		}
+	}
+
+	public String[] getTokens() {
+		return getTokens(0);
+	}
+
+	private String [] getTokens(int cnt){
+		String token = nextToken();
+		if ( token == null ) 
+			return new String[cnt];
+		
+		String result[] = getTokens(cnt+1);
+		result[cnt]=token;
+		return result;
+	}
+
+	public char getSeparator() { return separator; }
+	
+	public List<String> getTokenSet() {
+		List<String> list = Create.list();
+		String token = nextToken();
+		while ( token != null ) {
+			list.add(token);
+			token = nextToken();
+		}
+		return list;
+	}
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/reporter/Reporter.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/reporter/Reporter.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/reporter/Reporter.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,15 @@
+package aQute.libg.reporter;
+
+import java.util.*;
+
+
+public interface Reporter {
+	void error(String s, Object ... args);
+	void warning(String s, Object ... args);
+	void progress(String s, Object ... args);
+	void trace(String s, Object ... args);
+	List<String> getWarnings();
+	List<String> getErrors();
+	
+	boolean isPedantic();
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Replacer.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Replacer.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Replacer.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,5 @@
+package aQute.libg.sed;
+
+public interface Replacer {
+    String process(String line);
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Sed.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Sed.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/sed/Sed.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,82 @@
+package aQute.libg.sed;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+public class Sed {
+    final File                 file;
+    final Replacer             macro;
+    File                       output;
+
+    final Map<Pattern, String> replacements = new LinkedHashMap<Pattern, String>();
+
+    public Sed(Replacer macro, File file) {
+        assert file.isFile();
+        this.file = file;
+        this.macro = macro;
+    }
+
+    public void setOutput(File f) {
+        output = f;
+    }
+
+    public void replace(String pattern, String replacement) {
+        replacements.put(Pattern.compile(pattern), replacement);
+    }
+
+    public void doIt() throws IOException {
+        BufferedReader brdr = new BufferedReader(new FileReader(file));
+        File out;
+        if (output != null)
+            out = output;
+        else
+            out = new File(file.getAbsolutePath() + ".tmp");
+        File bak = new File(file.getAbsolutePath() + ".bak");
+        PrintWriter pw = new PrintWriter(new FileWriter(out));
+        try {
+            String line;
+            while ((line = brdr.readLine()) != null) {
+                for (Pattern p : replacements.keySet()) {
+                    String replace = replacements.get(p);
+                    Matcher m = p.matcher(line);
+
+                    StringBuffer sb = new StringBuffer();
+                    while (m.find()) {
+                        String tmp = setReferences(m, replace);
+                        tmp = macro.process(tmp);
+                        m.appendReplacement(sb, Matcher.quoteReplacement(tmp));
+                    }
+                    m.appendTail(sb);
+
+                    line = sb.toString();
+                }
+                pw.println(line);
+            }
+            pw.close();
+            if (output == null) {
+                file.renameTo(bak);
+                out.renameTo(file);
+            }
+        } finally {
+            brdr.close();
+            pw.close();
+        }
+    }
+
+    private String setReferences(Matcher m, String replace) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < replace.length(); i++) {
+            char c = replace.charAt(i);
+            if (c == '$' && i < replace.length() - 1
+                    && Character.isDigit(replace.charAt(i + 1))) {
+                int n = replace.charAt(i + 1) - '0';
+                if ( n <= m.groupCount() )
+                    sb.append(m.group(n));
+                i++;
+            } else
+                sb.append(c);
+        }
+        return sb.toString();
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/Version.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/Version.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/Version.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,148 @@
+package aQute.libg.version;
+
+import java.util.regex.*;
+
+public class Version implements Comparable<Version> {
+    final int                   major;
+    final int                   minor;
+    final int                   micro;
+    final String                qualifier;
+    public final static String  VERSION_STRING = "(\\d+)(\\.(\\d+)(\\.(\\d+)(\\.([-_\\da-zA-Z]+))?)?)?";
+    public final static Pattern VERSION        = Pattern
+                                                       .compile(VERSION_STRING);
+    public final static Version LOWEST         = new Version();
+    public final static Version HIGHEST        = new Version(Integer.MAX_VALUE,
+                                                       Integer.MAX_VALUE,
+                                                       Integer.MAX_VALUE,
+                                                       "\uFFFF");
+
+    public Version() {
+        this(0);
+    }
+
+    public Version(int major, int minor, int micro, String qualifier) {
+        this.major = major;
+        this.minor = minor;
+        this.micro = micro;
+        this.qualifier = qualifier;
+    }
+
+    public Version(int major, int minor, int micro) {
+        this(major, minor, micro, null);
+    }
+
+    public Version(int major, int minor) {
+        this(major, minor, 0, null);
+    }
+
+    public Version(int major) {
+        this(major, 0, 0, null);
+    }
+
+    public Version(String version) {
+        Matcher m = VERSION.matcher(version);
+        if (!m.matches())
+            throw new IllegalArgumentException("Invalid syntax for version: "
+                    + version);
+
+        major = Integer.parseInt(m.group(1));
+        if (m.group(3) != null)
+            minor = Integer.parseInt(m.group(3));
+        else
+            minor = 0;
+
+        if (m.group(5) != null)
+            micro = Integer.parseInt(m.group(5));
+        else
+            micro = 0;
+
+        qualifier = m.group(7);
+    }
+
+    public int getMajor() {
+        return major;
+    }
+
+    public int getMinor() {
+        return minor;
+    }
+
+    public int getMicro() {
+        return micro;
+    }
+
+    public String getQualifier() {
+        return qualifier;
+    }
+
+    public int compareTo(Version other) {
+        if (other == this)
+            return 0;
+
+        if (!(other instanceof Version))
+            throw new IllegalArgumentException(
+                    "Can only compare versions to versions");
+
+        Version o = (Version) other;
+        if (major != o.major)
+            return major - o.major;
+
+        if (minor != o.minor)
+            return minor - o.minor;
+
+        if (micro != o.micro)
+            return micro - o.micro;
+
+        int c = 0;
+        if (qualifier != null)
+            c = 1;
+        if (o.qualifier != null)
+            c += 2;
+
+        switch (c) {
+        case 0:
+            return 0;
+        case 1:
+            return 1;
+        case 2:
+            return -1;
+        }
+        return qualifier.compareTo(o.qualifier);
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(major);
+        sb.append(".");
+        sb.append(minor);
+        sb.append(".");
+        sb.append(micro);
+        if (qualifier != null) {
+            sb.append(".");
+            sb.append(qualifier);
+        }
+        return sb.toString();
+    }
+
+    public boolean equals(Object ot) {
+        if ( ! (ot instanceof Version))
+            return false;
+        
+        return compareTo((Version)ot) == 0;
+    }
+
+    public int hashCode() {
+        return major * 97 ^ minor * 13 ^ micro
+                + (qualifier == null ? 97 : qualifier.hashCode());
+    }
+
+    public int get(int i) {
+        switch(i) {
+        case 0 : return major;
+        case 1 : return minor;
+        case 2 : return micro;
+        default:
+            throw new IllegalArgumentException("Version can only get 0 (major), 1 (minor), or 2 (micro)");
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/VersionRange.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/VersionRange.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/java/aQute/libg/version/VersionRange.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,85 @@
+package aQute.libg.version;
+
+import java.util.regex.*;
+
+public class VersionRange {
+	Version			high;
+	Version			low;
+	char			start	= '[';
+	char			end		= ']';
+
+	static Pattern	RANGE	= Pattern.compile("(\\(|\\[)\\s*(" +
+									Version.VERSION_STRING + ")\\s*,\\s*(" +
+									Version.VERSION_STRING + ")\\s*(\\)|\\])");
+
+	public VersionRange(String string) {
+		string = string.trim();
+		Matcher m = RANGE.matcher(string);
+		if (m.matches()) {
+			start = m.group(1).charAt(0);
+			String v1 = m.group(2);
+			String v2 = m.group(10);
+			low = new Version(v1);
+			high = new Version(v2);
+			end = m.group(18).charAt(0);
+			if (low.compareTo(high) > 0)
+				throw new IllegalArgumentException(
+						"Low Range is higher than High Range: " + low + "-" +
+								high);
+
+		} else
+			high = low = new Version(string);
+	}
+
+	public boolean isRange() {
+		return high != low;
+	}
+
+	public boolean includeLow() {
+		return start == '[';
+	}
+
+	public boolean includeHigh() {
+		return end == ']';
+	}
+
+	public String toString() {
+		if (high == low)
+			return high.toString();
+
+		StringBuffer sb = new StringBuffer();
+		sb.append(start);
+		sb.append(low);
+		sb.append(',');
+		sb.append(high);
+		sb.append(end);
+		return sb.toString();
+	}
+
+	public Version getLow() {
+		return low;
+	}
+
+	public Version getHigh() {
+		return high;
+	}
+
+	public boolean includes(Version v) {
+		if ( !isRange() ) {
+			return low.compareTo(v) <=0;
+		}
+		if (includeLow()) {
+			if (v.compareTo(low) < 0)
+				return false;
+		} else if (v.compareTo(low) <= 0)
+			return false;
+
+		if (includeHigh()) {
+			if (v.compareTo(high) > 0)
+				return false;
+		} else if (v.compareTo(high) >= 0)
+			return false;
+		
+		return true;
+	}
+}
\ No newline at end of file

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/active.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/active.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/bundle.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/bundle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/export-package.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/export-package.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/fragment.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/fragment.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/host.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/host.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/import-package.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/import-package.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/importing-bundle.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/importing-bundle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/installed.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/installed.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/inuse.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/inuse.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/logo16x16.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/logo16x16.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/mini-logo.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/mini-logo.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/module.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/module.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/property.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/property.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/registered.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/registered.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/resolved.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/resolved.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/run.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/run.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/test.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/test.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/unknown.gif
===================================================================
(Binary files differ)


Property changes on: projects/jboss-osgi/projects/aQute/trunk/bnd/src/main/resources/icons/unknown.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: projects/jboss-osgi/projects/aQute/trunk/pom.xml
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/pom.xml	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/pom.xml	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+  <!-- ====================================================================== -->
+  <!--                                                                        -->
+  <!--  JBoss, the OpenSource J2EE webOS                                      -->
+  <!--                                                                        -->
+  <!--  Distributable under LGPL license.                                     -->
+  <!--  See terms of license at http://www.gnu.org.                           -->
+  <!--                                                                        -->
+  <!-- ====================================================================== -->
+
+  <!-- $Id: pom.xml 93592 2009-09-16 09:00:08Z thomas.diesler at jboss.com $ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <name>aQute</name>
+
+  <groupId>biz.aQute</groupId>
+  <artifactId>parent</artifactId>
+  <packaging>pom</packaging>
+
+  <version>0.0.356-SNAPSHOT</version>
+
+  <!-- Properties -->
+  <properties>
+    <version.apache.ant>1.7.0</version.apache.ant>
+    <version.junit>3.8.1</version.junit>
+    <version.osgi>r4v42</version.osgi>
+  </properties>
+
+  <!-- Modules -->
+  <modules>
+    <module>runtime</module>
+    <module>bnd</module>
+  </modules>
+
+  <!-- DependencyManagement -->
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.ant</groupId>
+        <artifactId>ant</artifactId>
+        <version>${version.apache.ant}</version>
+      </dependency>
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>${version.junit}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.osgi</groupId>
+        <artifactId>org.osgi.core</artifactId>
+        <version>${version.osgi}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.osgi</groupId>
+        <artifactId>org.osgi.compendium</artifactId>
+        <version>${version.osgi}</version>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <!-- Build -->
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+          <showDeprecation>true</showDeprecation>
+          <showWarnings>true</showWarnings>
+          <optimize>true</optimize>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+            </manifest>
+            <manifestEntries>
+              <Implementation-URL>${url}</Implementation-URL>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+        <inherited>true</inherited>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <redirectTestOutputToFile>false</redirectTestOutputToFile>
+          <failIfNoTests>false</failIfNoTests>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <quiet>true</quiet>
+          <excludePackageNames>*.internal</excludePackageNames>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <!-- Repositories -->
+  <repositories>
+    <repository>
+      <id>repository.jboss.org</id>
+      <name>JBoss Repository</name>
+      <url>http://repository.jboss.org/maven2/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>aQute</id>
+      <url>http://www.aQute.biz/repo</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+</project>

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/.classpath
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/.classpath	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/.classpath	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/.project
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/.project	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/.project	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>runtime</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.maven.ide.eclipse.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.maven.ide.eclipse.maven2Nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.eclipse.jdt.core.prefs	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.eclipse.jdt.core.prefs	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,5 @@
+#Mon Sep 28 14:56:59 CEST 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.source=1.5

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.maven.ide.eclipse.prefs
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.maven.ide.eclipse.prefs	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/.settings/org.maven.ide.eclipse.prefs	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,9 @@
+#Mon Sep 28 13:24:35 CEST 2009
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/pom.xml
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/pom.xml	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/pom.xml	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+  <!-- ====================================================================== -->
+  <!--                                                                        -->
+  <!--  JBoss, the OpenSource J2EE webOS                                      -->
+  <!--                                                                        -->
+  <!--  Distributable under LGPL license.                                     -->
+  <!--  See terms of license at http://www.gnu.org.                           -->
+  <!--                                                                        -->
+  <!-- ====================================================================== -->
+
+  <!-- $Id: pom.xml 93592 2009-09-16 09:00:08Z thomas.diesler at jboss.com $
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <name>aQute - Runtime</name>
+
+  <groupId>biz.aQute</groupId>
+  <artifactId>runtime</artifactId>
+  <packaging>jar</packaging>
+
+  <!-- Parent -->
+  <parent>
+    <groupId>biz.aQute</groupId>
+    <artifactId>parent</artifactId>
+    <version>0.0.356-SNAPSHOT</version>
+  </parent>
+
+  <!-- Dependencies -->
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+    </dependency>
+  </dependencies>
+
+</project>

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/BasicTestReport.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/BasicTestReport.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/BasicTestReport.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,75 @@
+package aQute.junit.runtime;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import junit.framework.*;
+
+import org.osgi.framework.*;
+
+class BasicTestReport implements TestListener {
+    int            errors;
+    boolean        verbose = true;
+    private Bundle targetBundle;
+    
+    public void begin(GenericFramework fw, Bundle targetBundle, List tests, int realcount) {
+        this.targetBundle = targetBundle;
+        if (verbose) {
+            System.out
+                    .println("====================================================================");
+        }
+    }
+
+    public void addError(Test test, Throwable t) {
+        errors++;
+        if (verbose) {
+            System.out.println(test + " : ");
+            t.printStackTrace(System.out);
+            System.out.println();
+        }
+    }
+
+    public void addFailure(Test test, AssertionFailedError t) {
+        errors++;
+        if (verbose) {
+            System.out.println();
+            System.out.print(test + " : ");
+            t.getMessage();
+        }
+    }
+
+    public void endTest(Test test) {
+        if (verbose) {
+            System.out.print("<< " + test + "\n");
+        }
+    }
+
+    public void startTest(Test test) {
+        try {
+            
+            Method m = test.getClass().getMethod("setBundleContext",
+                    new Class[] { BundleContext.class });
+            m.invoke(test, new Object[] { targetBundle.getBundleContext() });            
+        } catch (Exception e) {
+            // Ok, no problem
+        }
+        if (verbose)
+            System.out.println(">> " + test + "\n");
+    }
+
+    public void end() {
+        if (verbose) {
+            System.out
+                    .println("-------------------------------------------------------------------------");
+            if (errors == 0)
+                System.out.println("No errors :-)");
+            else if (errors == 1)
+                System.out.println("One error :-|");
+            else
+                System.out.println(errors + " errors :-(");
+            System.out.println();
+            System.out.println();
+        }
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/GenericFramework.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/GenericFramework.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/GenericFramework.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,479 @@
+package aQute.junit.runtime;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+
+import org.osgi.framework.*;
+import org.osgi.framework.launch.*;
+import org.osgi.service.permissionadmin.*;
+
+import aQute.junit.runtime.minifw.*;
+
+public class GenericFramework {
+    private String                 framework;
+    private final List            /* <File> */jars             = new ArrayList/* <File> */();
+    private final List            /* <File> */bundles          = new ArrayList/* <File> */();
+    private final List            /* <String> */systemPackages = new ArrayList/* <String> */();
+    private Framework              systemBundle;
+    private File                   storage;
+    private boolean                keep;
+    private final Properties       properties;
+    private boolean                security;
+    private SimplePermissionPolicy policy;
+
+    public GenericFramework(Properties properties) {
+        this.properties = properties;
+        systemPackages.add("org.osgi.framework");
+        systemPackages.add("org.osgi.framework.launch");
+    }
+
+    public boolean activate() throws Exception {
+        boolean error = false;
+        Policy.setPolicy(new AllPolicy());
+
+        systemBundle = createFramework();
+        if (systemBundle == null)
+            return false;
+
+        try {
+            PermissionInfo         allPermissions[]          = new PermissionInfo[] { new PermissionInfo(
+                    AllPermission.class
+                            .getName(),
+                    null, null) };
+            policy = new SimplePermissionPolicy(systemBundle.getBundleContext());
+
+            // All bundles installed from the script are getting AllPermission
+            // for now.
+            policy.setDefaultPermissions(allPermissions);
+            security = true;
+        } catch (Throwable t) {
+            // This can throw a linkage error when the framework
+            // does not carry the PermissionAdmin class
+            security = false;
+        }
+
+        systemBundle.start();
+        // Initialize this framework so it becomes STARTING
+        BundleContext systemContext = getFrameworkContext();
+
+        // Install the set of bundles
+        List/* <Bundle> */installed = new ArrayList/* <Bundle> */();
+
+        for (Iterator/* <File> */i = bundles.iterator(); i.hasNext();) {
+            File path = ((File) i.next()).getAbsoluteFile();
+
+            InputStream in = new FileInputStream(path);
+            try {
+                Bundle bundle = systemContext
+                        .installBundle(path.toString(), in);
+                installed.add(bundle);
+            } catch (BundleException e) {
+                System.out.println("Install: " + path + " ");
+                report(e, System.out);
+                error = true;
+            } finally {
+                in.close();
+            }
+        }
+
+        // From now on, the bundles are on their own. They have
+        // by default AllPermission, but if they install bundles
+        // they will not automatically get AllPermission anymore
+        
+        if (security)
+            policy.setDefaultPermissions(null);
+
+        // Now start all the installed bundles in the same order
+        // (unless they're a fragment)
+        
+        for (Iterator/* <Bundle> */i = installed.iterator(); i.hasNext();) {
+            Bundle b = (Bundle) i.next();
+            try {
+                if (!isFragment(b))
+                    b.start();
+            } catch (BundleException e) {
+                System.out.println("Start: " + b.getBundleId() + " ");
+                report(e, System.out);
+                error = true;
+            }
+        }
+        return !error;
+    }
+
+    private boolean isFragment(Bundle b) {
+        return b.getHeaders().get("Fragment-Host") != null;
+    }
+
+    public void deactivate() throws Exception {
+        if (systemBundle != null) {
+            getFrameworkBundle().stop();
+            waitForStop(0);
+        }
+    }
+
+    public void addSystemPackage(String packageName) {
+        systemPackages.add(packageName);
+    }
+
+    private Framework createFramework() throws Exception {
+        Properties p = new Properties();
+        p.putAll(properties);
+
+        if (storage != null) {
+            if (!keep)
+                delete(storage);
+
+            storage.mkdirs();
+            if (!storage.isDirectory())
+                throw new IllegalArgumentException();
+
+            p.setProperty(Constants.FRAMEWORK_STORAGE, storage
+                    .getAbsolutePath());
+        }
+
+        if (!systemPackages.isEmpty())
+            p.setProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
+                    toPackages(systemPackages));
+
+        URL urls[] = new URL[jars.size()];
+        for (int i = 0; i < jars.size(); i++) {
+            urls[i] = ((File) jars.get(i)).toURL();
+        }
+
+        URLClassLoader loader = new URLClassLoader(urls, getClass()
+                .getClassLoader());
+
+        try {
+            Framework systemBundle;
+
+            // 1) framework = Framework class name as originally defined
+            // 2) framework = Framework Factory class name, new!
+            // 3) framework = null, lookup in META-INF/services
+
+            if (p.containsKey("noframework")) {
+                systemBundle = new MiniFramework(p);
+            } else if (framework != null) {
+                Class/* <?> */clazz = loader.loadClass(framework);
+
+                if (FrameworkFactory.class.isAssignableFrom(clazz)) {
+                    // 2) Specified factory name
+                    FrameworkFactory f = (FrameworkFactory) clazz.newInstance();
+                    systemBundle = f.newFramework(p);
+                } else if (Framework.class.isAssignableFrom(clazz)) {
+                    // 1) Specified framework name
+                    Constructor/* <?> */ctor = clazz
+                            .getConstructor(new Class[] { Map.class });
+
+                    systemBundle = (Framework) ctor
+                            .newInstance(new Object[] { p });
+                } else
+                    throw new IllegalArgumentException(
+                            "Specified framework class is not an instance class nor a framework factory");
+
+            } else {
+                // 3) Lookup in META-INF/services
+                List implementations = getMetaInfServices(loader,
+                        FrameworkFactory.class.getName());
+                if (implementations.size() != 1)
+                    System.out
+                            .println("Found more than one framework implementatios: "
+                                    + implementations);
+
+                String implementation = (String) implementations.get(0);
+
+                Class/* <?> */clazz = loader.loadClass(implementation);
+                FrameworkFactory factory = (FrameworkFactory) clazz
+                        .newInstance();
+                systemBundle = factory.newFramework(p);
+            }
+            systemBundle.init();
+            return systemBundle;
+        } catch (ClassNotFoundException cnfe) {
+            System.out
+                    .println("Can not load the framework class: " + framework);
+            return null;
+        } catch (NoSuchMethodException nsme) {
+            System.out
+                    .println("Can not find RFC 132 constructor <init>(Map) in "
+                            + framework);
+            return null;
+        } catch (InvocationTargetException e) {
+            System.out.println("Error in constructing framework");
+            e.getCause().printStackTrace();
+            throw e;
+        }
+    }
+
+    /**
+     * Try to get the stupid service interface ...
+     * 
+     * @param loader
+     * @param string
+     * @return
+     * @throws IOException
+     */
+    private List getMetaInfServices(ClassLoader loader, String factory)
+            throws IOException {
+        if (loader == null)
+            loader = getClass().getClassLoader();
+
+        Enumeration e = loader.getResources("META-INF/services/" + factory);
+        List factories = new ArrayList();
+
+        while (e.hasMoreElements()) {
+            URL url = (URL) e.nextElement();
+            BufferedReader rdr = new BufferedReader(new InputStreamReader(url
+                    .openStream()));
+            String line;
+            while ((line = rdr.readLine()) != null) {
+                line = line.trim();
+                if (!line.startsWith("#") && line.length() > 0) {
+                    factories.add(line);
+                }
+            }
+        }
+        return factories;
+    }
+
+    private String toPackages(List/* <String> */packs) {
+        String del = "";
+        StringBuffer sb = new StringBuffer();
+        for (Iterator i = packs.iterator(); i.hasNext();) {
+            String s = (String) i.next();
+            sb.append(del);
+            sb.append(s);
+            del = ", ";
+        }
+        return sb.toString();
+    }
+
+    public void addBundle(File resource) {
+        bundles.add(resource);
+    }
+
+    public void addJar(File resource) {
+        jars.add(resource);
+    }
+
+    public BundleContext getFrameworkContext() {
+        return systemBundle.getBundleContext();
+    }
+
+    public Framework getFrameworkBundle() {
+        return systemBundle;
+    }
+
+    public void setFramework(String framework) {
+        this.framework = framework;
+    }
+
+    public String getFramework() {
+        return framework;
+    }
+
+    public BundleContext getBundleContext() {
+        return systemBundle.getBundleContext();
+    }
+
+    public void waitForStop(long time) throws Exception {
+        getFrameworkBundle().waitForStop(time);
+    }
+
+    public Bundle getBundle(String target) {
+        Bundle bundles[] = getBundleContext().getBundles();
+        for (int i = 0; i < bundles.length; i++) {
+            if (bundles[i].getLocation().equals(target)) {
+                return bundles[i];
+            }
+        }
+        return null;
+    }
+
+    public void setKeep() {
+        this.keep = true;
+    }
+
+    public void setStorage(File storage) {
+        this.storage = storage;
+    }
+
+    void delete(File f) {
+        if (f.getAbsolutePath().matches("(/|[a-zA-Z]:\\\\*|\\\\)"))
+            throw new IllegalArgumentException(
+                    "You can not make the root the storage area because it will be deleted");
+        if (f.isDirectory()) {
+            File fs[] = f.listFiles();
+            for (int i = 0; i < fs.length; i++)
+                delete(fs[i]);
+        }
+        f.delete();
+    }
+
+    public void report(PrintStream out) {
+        try {
+            System.out
+                    .println("------------------------------- REPORT --------------------------");
+            out.println();
+            out.println("Framework             " + framework);
+            out.println("Framework             "
+                    + (systemBundle == null ? "<>" : systemBundle.getClass()
+                            .toString()));
+            out.println("Storage               " + storage);
+            out.println("Keep                  " + keep);
+            out.println("Security              " + security);
+            list("Jars                  ", jars);
+            list("System Packages       ", systemPackages);
+            list("Classpath             ", Arrays.asList(System.getProperty(
+                    "java.class.path").split(File.pathSeparator)));
+            out.println("Properties");
+            for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
+                Map.Entry entry = (Map.Entry) i.next();
+                String key = (String) entry.getKey();
+                String value = (String) entry.getValue();
+                out.print(fill(key, 40));
+                out.println(value);
+            }
+            if (systemBundle != null) {
+                BundleContext context = systemBundle.getBundleContext();
+                if (context != null) {
+                    Bundle bundles[] = context.getBundles();
+                    System.out.println();
+                    System.out.println("Id    State Modified      Location");
+
+                    for (int i = 0; i < bundles.length; i++) {
+                        File f = new File(bundles[i].getLocation());
+                        out.print(fill(Long.toString(bundles[i].getBundleId()),
+                                6));
+                        out.print(fill(toState(bundles[i].getState()), 6));
+                        if (f.exists())
+                            out.print(fill(toDate(f.lastModified()), 14));
+                        else
+                            out.print(fill("<>", 14));
+                        out.println(bundles[i].getLocation());
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            out.println("Sorry, can't print framework: " + t);
+        }
+    }
+
+    String toDate(long t) {
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(t);
+        return fill(c.get(Calendar.YEAR), 4) + fill(c.get(Calendar.MONTH), 2)
+                + fill(c.get(Calendar.DAY_OF_MONTH), 2)
+                + fill(c.get(Calendar.HOUR_OF_DAY), 2)
+                + fill(c.get(Calendar.MINUTE), 2);
+    }
+
+    private String fill(int n, int width) {
+        return fill(Integer.toString(n), width, '0', -1);
+    }
+
+    private String fill(String s, int width) {
+        return fill(s, width, ' ', -1);
+    }
+
+    private String fill(String s, int width, char filler, int dir) {
+        StringBuffer sb = new StringBuffer();
+        if (s.length() > width)
+            s = s.substring(0, width - 2) + "..";
+
+        width -= s.length();
+        int before = (dir == 0) ? width / 2 : (dir < 0) ? 0 : width;
+        int after = width - before;
+
+        while (before-- > 0)
+            sb.append(filler);
+
+        sb.append(s);
+
+        while (after-- > 0)
+            sb.append(filler);
+
+        return sb.toString();
+    }
+
+    private String toState(int state) {
+        switch (state) {
+        case Bundle.INSTALLED:
+            return "INSTL";
+        case Bundle.RESOLVED:
+            return "RSLVD";
+        case Bundle.STARTING:
+            return "STRTD";
+        case Bundle.STOPPING:
+            return "STPPD";
+        case Bundle.ACTIVE:
+            return "ACTIV";
+        case Bundle.UNINSTALLED:
+            return "UNNST";
+        }
+        return "? " + state;
+    }
+
+    private void list(String del, List l) {
+        for (Iterator i = l.iterator(); i.hasNext();) {
+            String s = i.next().toString();
+            System.out.println(del + s);
+            del = "                                                                       "
+                    .substring(0, del.length());
+        }
+    }
+
+    public static void report(BundleException e, PrintStream out) {
+        switch (e.getType()) {
+        case BundleException.ACTIVATOR_ERROR:
+            System.out.println("Caused by in activator: ");
+            e.getCause().printStackTrace();
+            break;
+
+        default:
+        case BundleException.DUPLICATE_BUNDLE_ERROR:
+        case BundleException.INVALID_OPERATION:
+        case BundleException.MANIFEST_ERROR:
+        case BundleException.NATIVECODE_ERROR:
+        case BundleException.STATECHANGE_ERROR:
+        case BundleException.UNSUPPORTED_OPERATION:
+        case BundleException.UNSPECIFIED:
+        case BundleException.RESOLVE_ERROR:
+            System.out.println(e.getMessage());
+            break;
+        }
+    }
+
+    static class AllPolicy extends Policy {
+        static PermissionCollection all = new AllPermissionCollection();
+
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return all;
+        }
+
+        public void refresh() {
+        }
+    }
+
+    static class AllPermissionCollection extends PermissionCollection {
+        private static final long serialVersionUID = 1L;
+        private static Vector     list             = new Vector();
+
+        {
+            setReadOnly();
+        }
+
+        public void add(Permission permission) {
+        }
+
+        public Enumeration elements() {
+            return list.elements();
+        }
+
+        public boolean implies(Permission permission) {
+            return true;
+        }
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/JUnitReport.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/JUnitReport.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/JUnitReport.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,123 @@
+package aQute.junit.runtime;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+
+import junit.framework.*;
+
+import org.osgi.framework.*;
+
+public class JUnitReport implements TestReporter {
+    int            port;
+    boolean        open;
+    BufferedReader in;
+    PrintWriter    out;
+    long           startTime;
+    GenericFramework      framework;
+    Bundle         targetBundle;
+    List           tests;
+    boolean        verbose = false;
+
+    public JUnitReport(int port) throws Exception {
+        Socket socket = null;
+        for (int i = 0; socket == null && i < 10; i++) {
+            try {
+                socket = new Socket("127.0.0.1", port);
+            } catch (ConnectException ce) {
+                Thread.sleep(i * 100);
+            }
+        }
+        if (socket == null)
+            System.exit(-1);
+
+        in = new BufferedReader(new InputStreamReader(socket.getInputStream(),
+                "UTF-8"));
+        out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(),
+                "UTF-8"));
+    }
+
+    public void begin(GenericFramework fw, Bundle targetBundle, List tests, int realcount) {
+        this.framework = fw;
+        this.targetBundle = targetBundle;
+        this.tests = tests;
+        message("%TESTC  ", realcount + " v2");
+        report(tests);
+        startTime = System.currentTimeMillis();
+    }
+
+    public void end() {
+        message("%RUNTIME", "" + (System.currentTimeMillis() - startTime));
+        out.flush();
+        out.close();
+        try {
+            in.close();
+        } catch (Exception ioe) {
+            // ignore
+        }
+    }
+
+    public void addError(Test test, Throwable t) {
+        message("%ERROR  ", test);
+        trace(t);
+    }
+
+    public void addFailure(Test test, AssertionFailedError t) {
+        message("%FAILED ", test);
+        trace(t);
+    }
+
+    void trace(Throwable t) {
+        message("%TRACES ", "");
+        t.printStackTrace(out);
+        out.println();
+        message("%TRACEE ", "");
+    }
+
+    public void endTest(Test test) {
+        message("%TESTE  ", test);
+    }
+
+    public void startTest(Test test) {
+        message("%TESTS  ", test);
+        try {
+            Method m = test.getClass().getMethod("setBundleContext",
+                    new Class[] { BundleContext.class });
+            m.invoke(test, new Object[] { targetBundle.getBundleContext() });
+        } catch (Exception e) {
+
+        }
+    }
+
+    private void message(String key, String payload) {
+        if (key.length() != 8)
+            throw new IllegalArgumentException(key + " is not 8 characters");
+
+        out.print(key);
+        out.println(payload);
+        out.flush();
+        if (verbose)
+            System.out.println(key + payload);
+    }
+
+    private void message(String key, Test test) {
+        message(key, (tests.indexOf(test) + 1) + "," + test);
+    }
+
+    private void report(List flattened) {
+        for (int i = 0; i < flattened.size(); i++) {
+            StringBuffer sb = new StringBuffer();
+            sb.append(i + 1);
+            sb.append(",");
+            Test test = (Test) flattened.get(i);
+            sb.append(flattened.get(i));
+            sb.append(",");
+            sb.append(test instanceof TestSuite);
+            sb.append(",");
+            sb.append(test.countTestCases());
+            message("%TSTTREE", sb.toString());
+        }
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/SimplePermissionPolicy.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/SimplePermissionPolicy.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/SimplePermissionPolicy.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,193 @@
+package aQute.junit.runtime;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Vector;
+import org.osgi.framework.*;
+import org.osgi.service.permissionadmin.*;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Implements a permissionpolicy. It will tried to read a resource from the
+ * bundle. This resource should have the following format:
+ * 
+ * <pre>
+ * 
+ *  
+ *    '(' permission-class [ '&quot;' name-parameter '&quot;' [ '&quot;' action [ ',' action ] ... '&quot;' ] ] ')'
+ *   Or
+ *    '#' *
+ *   
+ *  
+ * </pre>
+ * 
+ * Each valid line is translated into a PermissionInfo object and these objects
+ * together form the permissions for a specific bundle. The class will also
+ * attempt to read a file called "default.perm" from the current bundle that
+ * will have the same format. This is used for the default permissions.
+ * <p>
+ * If there is no permission admin service, this class does nothing relevant.
+ */
+public class SimplePermissionPolicy implements SynchronousBundleListener {
+    static final String DEFAULT_PERMISSION_RESOURCE = "default.perm";
+    BundleContext       context;
+    ServiceTracker      tracker;
+    Vector              bundles;
+    PermissionInfo[]    defaultPermissions;
+
+    /**
+     * Create a new permission policy. This will set the default permissions and
+     * the permissions for this bundle (if the resource is present).
+     */
+    SimplePermissionPolicy(BundleContext context) throws Exception {
+        this.context = context;
+        bundles = new Vector();
+        tracker = new ServiceTracker(context, PermissionAdmin.class.getName(),
+                null);
+        tracker.open();
+        context.addBundleListener(this);
+        PermissionAdmin permissionAdmin = (PermissionAdmin) tracker
+                .getService();
+        if (permissionAdmin == null) /* no permission admin service */
+            return;
+        // Set the default permissions.
+        InputStream in = getClass().getResourceAsStream(
+                DEFAULT_PERMISSION_RESOURCE);
+        if (in != null) {
+            PermissionInfo[] info = parse(in);
+            permissionAdmin.setDefaultPermissions(info);
+        } else {
+            System.out.println("default permission not found "
+                    + DEFAULT_PERMISSION_RESOURCE);
+        }
+        //
+        // Set this bundles permissions.
+        //
+        Bundle self = context.getBundle();
+        setPermissions(self);
+    }
+
+    /**
+     * Sets the permissions of a bundle from a resource, if exists.
+     */
+    public void setPermissions(Bundle bundle) {
+        PermissionAdmin permissionAdmin = (PermissionAdmin) tracker
+                .getService();
+        if (permissionAdmin == null) /* no permission admin service */{
+            return;
+        }
+        PermissionInfo[] info = getPermissions(bundle);
+        if ( info == null )
+            info = defaultPermissions;
+        
+        if (info != null && info.length > 0) {
+            bundles.addElement(bundles);
+            System.out.print("Setting permission for " + bundle.getLocation() );
+            String del = "=[ ";
+            for ( int i =0; i<info.length; i++ ) {
+                System.out.print(del);
+                System.out.print(info[i]);
+                del = ", ";
+            }
+            System.out.println(" ]");
+            permissionAdmin.setPermissions(bundle.getLocation(), info);
+        } else 
+            System.out.println("No permissions for " + bundle.getLocation() );
+
+    }
+
+    /**
+     * Get the resource and parse it into PermissionInfo objects.
+     */
+    public PermissionInfo[] getPermissions(Bundle bundle) {        
+        URL    url = bundle.getEntry("/META-INF/permissions.perm");
+        if (url == null)
+            url = bundle.getEntry("/META-INF/permissions.perm".toUpperCase());
+
+        PermissionInfo[] info = null;
+        if (url != null)
+            try {
+                InputStream in = url.openStream();
+                info = parse(in);
+            } catch (IOException e) {
+                System.out
+                        .println("Unable to read permission info for bundle  "
+                                + bundle.getLocation() + " " + e);
+            }
+        return info;
+    }
+
+    /**
+     * Parse a permission info file.
+     */
+    public PermissionInfo[] parse(InputStream in) throws IOException {
+        PermissionInfo[] info = null;
+        if (in != null) {
+            Vector permissions = new Vector();
+            try {
+                BufferedReader reader = new BufferedReader(
+                        new InputStreamReader(in));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if ((line.length() == 0) || line.startsWith("#")
+                            || line.startsWith("//")) /* comments */
+                        continue;
+                    try {
+                        permissions.addElement(new PermissionInfo(line));
+                    } catch (IllegalArgumentException iae) {
+                        /* incorrectly encoded permission */
+                        System.out.println("Permission incorrectly encoded: "
+                                + line + " " + iae);
+                    }
+                }
+            } finally {
+                in.close();
+            }
+            int size = permissions.size();
+            if (size > 0) {
+                info = new PermissionInfo[size];
+                permissions.copyInto(info);
+            }
+        }
+        return info;
+    }
+
+    /**
+     * Clear the permissions for a bundle.
+     */
+    public void clearPermissions(Bundle bundle) {
+        PermissionAdmin permissionAdmin = (PermissionAdmin) tracker
+                .getService();
+        if (permissionAdmin == null) /* no permission admin service */
+            return;
+        if (bundles.removeElement(bundle)) {
+            permissionAdmin.setPermissions(bundle.getLocation(), null);
+        }
+    }
+
+    /**
+     * Event when a bundle has changed so we need to inspect if it is installed,
+     * and if so we need to set the permissions or remove it when it is
+     * uninstalled.
+     */
+    public void bundleChanged(BundleEvent event) {
+        Bundle bundle = event.getBundle();
+        if (bundle.getBundleId() == 0) /* ignore the system bundle */
+            return;
+        int type = event.getType();
+        switch (type) {
+        case BundleEvent.INSTALLED:
+        case BundleEvent.UPDATED:
+            setPermissions(bundle);
+            break;
+        case BundleEvent.UNINSTALLED:
+            clearPermissions(bundle);
+            break;
+        }
+    }
+
+    public void setDefaultPermissions(PermissionInfo defaultPermissions[]) {
+        this.defaultPermissions = defaultPermissions;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/Target.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/Target.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/Target.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,243 @@
+package aQute.junit.runtime;
+
+import java.io.*;
+import java.util.*;
+
+import junit.framework.*;
+
+import org.osgi.framework.*;
+
+public class Target {
+    final List             testNames  = new ArrayList();
+    List                   flattened;
+    int                    port       = -1;
+    boolean                keepAlive;
+    String                 target;
+    boolean                deferred   = false;
+    boolean                clear      = false;
+    boolean                verbose    = true;
+    final Properties       properties = new Properties();
+    final GenericFramework framework  = new GenericFramework(properties);
+    String                 reportName = "test-report.xml";
+    boolean                waitForEver;
+
+    /*
+     * -version 3 -port 55310 -testLoaderClass
+     * org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestLoader
+     * -loaderpluginname org.eclipse.jdt.junit.runtime -testNameFile
+     * /tmp/testNames18041.txt
+     */
+
+    public static void main(String[] args) {
+        Target target = new Target();
+        System.exit(target.run(args));
+    }
+
+    int run(String args[]) {
+        try {
+            init(args);
+            if (port == -1)
+                return doTesting(new XMLReport(reportName));
+            else
+                return doTesting(new JUnitReport(port));
+        } catch (Throwable e) {
+            error("bnd runtime", e);
+            framework.report(System.out);
+            return -1;
+        }
+    }
+
+    void init(String args[]) throws Exception {
+        for (int i = 0; i < args.length; i++) {
+            if ("-version".equals(args[i])) {
+                if (!"3".equals(args[++i].trim()))
+                    throw new RuntimeException(
+                            "This target only works with JUnit protocol #3");
+            } else if ("-port".equals(args[i]))
+                port = Integer.parseInt(args[++i]);
+            else if ("-deferred".equals(args[i])) {
+                deferred = true;
+            } else if ("-clear".equals(args[i])) {
+                clear = true;
+            } else if ("-testNameFile".equals(args[i]))
+                processFile(new File(args[++i]));
+            else if ("-testLoaderClass".equals(args[i])) // From old
+                // interface
+                i++;
+            else if ("-loaderpluginname".equals(args[i])) // From
+                // old
+                // interface
+                i++;
+            else if ("-test".equals(args[i]))
+                testNames.add(args[++i]);
+            else if ("-classNames".equals(args[i]))
+                testNames.add(args[++i]);
+            else if ("-bundle".equals(args[i]))
+                framework.addBundle(new File(args[++i]));
+            else if ("-export".equals(args[i]))
+                framework.addSystemPackage(args[++i]);
+            else if ("-framework".equals(args[i]))
+                framework.setFramework(args[++i]);
+            else if ("-keepalive".equals(args[i]))
+                keepAlive = true;
+            else if ("-set".equals(args[i])) {
+                properties.setProperty(args[++i], args[++i]);
+            } else if ("-target".equals(args[i])) {
+                target = args[++i];
+                framework.addBundle(new File(target));
+            } else if ("-storage".equals(args[i])) {
+                framework.setStorage(new File(args[++i]));
+            } else if ("-report".equals(args[i]))
+                reportName = args[++i];
+            else if ("-verbose".equals(args[i])) {
+                verbose = true;
+                System.out.println("bnd OSGi Runtime");
+            } else
+                warning("Do not understand arg: " + args[i]);
+        }
+        System.getProperties().putAll(properties);
+    }
+
+    /**
+     * We are not started from JUnit. This means we start the environment,
+     * install, the bundles, and run. If the target bundle has a Test-Cases
+     * header, we will run JUnit tests on that class.
+     */
+    void checkTestCases(Bundle bundle) throws Throwable {
+        String testcases = (String) bundle.getHeaders().get("Test-Cases");
+        if (testcases == null)
+            return;
+
+        String[] classes = testcases.split("\\s*,\\s*");
+        for (int i = 0; i < classes.length; i++)
+            testNames.add(classes[i]);
+    }
+
+    /**
+     * Main test routine.
+     * 
+     * @param tl
+     * @param framework
+     * @param targetBundle
+     * @param testNames
+     * @return
+     * @throws Throwable
+     */
+    private int doTesting(TestReporter tl) throws Throwable {
+        if (framework.activate()) {
+            boolean report = properties.containsKey("report");
+            if (report)
+                framework.report(System.out);
+
+            Bundle targetBundle = framework.getBundle(target);
+            if (targetBundle == null)
+                throw new IllegalArgumentException("No target specified");
+
+            // Verify if we have any test names set
+            if (testNames.size() == 0)
+                checkTestCases(targetBundle);
+
+            if (testNames.size() == 0) {
+                System.out
+                        .println("No test cases to run, waiting for the framework to quit");
+                framework.waitForStop(0);
+                System.out.println("And the framework is gone!");
+                return 0;
+            }
+
+            BasicTestReport otl = new BasicTestReport();
+            TestResult result = new TestResult();
+            try {
+                TestSuite suite = createSuite(targetBundle, testNames);
+                List flattened = new ArrayList();
+                int realcount = flatten(flattened, suite);
+                tl.begin(framework, targetBundle, flattened, realcount);
+                otl.begin(framework, targetBundle, flattened, realcount);
+                result.addListener(tl);
+                result.addListener(otl);
+                suite.run(result);
+                if (result.wasSuccessful())
+                    return 0;
+                else
+                    return otl.errors;
+            } catch (Throwable t) {
+                result.addError(null, t);
+                throw t;
+            } finally {
+                tl.end();
+                otl.end();
+                if (properties.containsKey("wait")) {
+                    framework.waitForStop(10000000);
+                }
+                framework.deactivate();
+            }
+        } else
+            throw new IllegalStateException("Framework does not activate");
+    }
+
+    private int flatten(List list, TestSuite suite) {
+        int realCount = 0;
+        for (Enumeration e = suite.tests(); e.hasMoreElements();) {
+            Test test = (Test) e.nextElement();
+            list.add(test);
+            if (test instanceof TestSuite)
+                realCount += flatten(list, (TestSuite) test);
+            else
+                realCount++;
+        }
+        return realCount;
+    }
+
+    /**
+     * Convert the test names to a test suite.
+     * 
+     * @param tfw
+     * @param testNames
+     * @return
+     * @throws Exception
+     */
+    private TestSuite createSuite(Bundle tfw, List testNames) throws Exception {
+        TestSuite suite = new TestSuite();
+        for (Iterator i = testNames.iterator(); i.hasNext();) {
+            String fqn = (String) i.next();
+            int n = fqn.indexOf(':');
+            if (n > 0) {
+                String method = fqn.substring(n + 1);
+                fqn = fqn.substring(0, n);
+                Class clazz = tfw.loadClass(fqn);
+                suite.addTest(TestSuite.createTest(clazz, method));
+            } else {
+                Class clazz = tfw.loadClass(fqn);
+                suite.addTestSuite(clazz);
+            }
+        }
+        return suite;
+    }
+
+    private void warning(String string) {
+        System.out.println("warning: " + string);
+    }
+
+    private void error(String string, Throwable e) {
+        if (e instanceof BundleException)
+            GenericFramework.report((BundleException) e, System.out);
+        else {
+            System.out.println(string + " : " + e);
+
+            if (verbose)
+                e.printStackTrace();
+        }
+    }
+
+    private void processFile(File file) throws IOException {
+        FileReader rdr = new FileReader(file);
+        BufferedReader brdr = new BufferedReader(rdr);
+        String line = brdr.readLine();
+        while (line != null) {
+            testNames.add(line.trim());
+            line = brdr.readLine();
+        }
+        rdr.close();
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/TestReporter.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/TestReporter.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/TestReporter.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,15 @@
+package aQute.junit.runtime;
+
+import java.util.*;
+
+import junit.framework.*;
+
+import org.osgi.framework.*;
+
+public interface TestReporter extends TestListener {
+
+    void begin(GenericFramework framework, Bundle targetBundle, List tests, int realcount);
+
+    void end();
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/XMLReport.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/XMLReport.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/XMLReport.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,93 @@
+package aQute.junit.runtime;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import junit.framework.*;
+
+import org.osgi.framework.*;
+
+public class XMLReport implements TestReporter {
+    final File        file;
+    final PrintStream out;
+    List             /* <Test> */tests;
+    boolean           open;
+
+    public XMLReport(String reportName) throws FileNotFoundException {
+        file = new File(reportName);
+        out = new PrintStream(new FileOutputStream(file));
+    }
+
+    public void begin(GenericFramework fw, Bundle targetBundle, List classNames, int realcount) {
+        out.println("<?xml version='1.0'?>");
+        out.println("<testreport" );
+        out.println("    target='"+targetBundle.getLocation()+"'");
+        out.println("    time='"+new Date()+"' ");
+        out.println("    framework='"+fw.getFramework()+"'>");
+        Bundle [] bundles = fw.getFrameworkBundle().getBundleContext().getBundles();
+        for ( int i =0; i<bundles.length; i++ ) {
+            out.println("  <bundle location='"+bundles[i].getLocation()+"' ");
+            out.println("     modified='"+bundles[i].getLastModified()+"' ");
+            out.println("     state='"+bundles[i].getState()+"' ");
+            out.println("     id='"+bundles[i].getBundleId()+"' ");
+            out.println("     bsn='"+bundles[i].getSymbolicName()+"' ");
+            out.println("     version='"+bundles[i].getHeaders().get("Bundle-Version")+"' ");
+            out.println("  />");
+        }
+    }
+
+    public void end() {
+        out.println("</testreport>");
+        out.close();
+    }
+
+    public void setTests(List flattened) {
+        this.tests = flattened;
+    }
+
+    static Pattern TEST_NAME = Pattern.compile("(.+)\\((.+)\\)");
+    public void startTest(Test test) {
+        String nameAndClass = test.toString();
+        String name = nameAndClass;
+        String clazz="";
+        Matcher m = TEST_NAME.matcher(nameAndClass);
+        if ( m .matches() ) {
+            name = m.group(1);
+            clazz=m.group(2);
+        }
+        out.print("  <test name='" + name + "' class='" + clazz +"'");
+        open = true;
+    }
+
+    public void addError(Test test, Throwable t) {
+        if (open)
+            out.println(">");
+        open = false;
+        out.println(" <error name='" + test.toString() + "' type='" + t.getClass().getName() + "'>");
+        out.println("<![CDATA[");
+        t.printStackTrace(out);
+        out.println("]]>");
+        out.println(" </error>");
+    }
+
+    public void addFailure(Test test, AssertionFailedError t) {
+        if (open)
+            out.println(">");
+        open = false;
+        out.println(" <failure name='" + test.toString() + "' type='" + t.getClass().getName() + "'>");
+        out.println("<![CDATA[");
+        t.printStackTrace(out);
+        out.println("]]>");
+        out.println(" </failure>");
+    }
+
+    public void endTest(Test test) {
+        if (open)
+            out.println("/>");
+        else
+            out.println("  </test>");
+        open = false;
+    }
+
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/Context.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/Context.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/Context.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,255 @@
+package aQute.junit.runtime.minifw;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+
+import org.osgi.framework.*;
+
+public class Context extends URLClassLoader implements Bundle, BundleContext {
+    long          id;
+    MiniFramework fw;
+    String        location;
+    int           state = Bundle.INSTALLED;
+    JarFile         jar;
+    Manifest manifest;
+    
+    class Dict extends Dictionary {
+
+        public Enumeration elements() {
+            return Collections.enumeration(manifest.getMainAttributes().values());
+        }
+
+        public Object get(Object key) {
+            return manifest.getMainAttributes().getValue((String)key);
+        }
+
+        public boolean isEmpty() {
+            return manifest.getMainAttributes().isEmpty();
+        }
+
+        public Enumeration keys() {
+            Vector v = new Vector();
+            for ( Iterator i = manifest.getMainAttributes().keySet().iterator(); i.hasNext(); ) {
+                Attributes.Name name = (Attributes.Name) i.next();
+                v.add(name.toString());
+            }
+            return v.elements();
+        }
+
+        public Object put(Object key, Object value) {
+            throw new UnsupportedOperationException();
+        }
+
+        public Object remove(Object key) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int size() {
+            return manifest.getMainAttributes().size();
+        }
+        
+    }
+    
+    Context(MiniFramework fw, ClassLoader parent, int id, String location) throws IOException {
+        super( new URL[]{ new File(location).toURL()}, parent);
+        this.fw = fw;
+        this.id = id;
+        this.location = location;
+        jar= new JarFile(new File(location));
+        manifest = jar.getManifest();
+        jar.close();
+    }
+
+    public BundleContext getBundleContext() {
+        return this;
+    }
+
+    public long getBundleId() {
+        return id;
+    }
+
+    public URL getEntry(String path) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Enumeration getEntryPaths(String path) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Dictionary getHeaders() {
+        return new Dict();
+    }
+
+    public Dictionary getHeaders(String locale) {
+        return new Dict();
+    }
+
+    public long getLastModified() {
+        return 0;
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public Enumeration findEntries(String path, String filePattern,
+            boolean recurse) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getRegisteredServices() {
+        return null;
+    }
+
+
+    public ServiceReference[] getServicesInUse() {
+        return null;
+    }
+
+    public Map getSignerCertificates(int signersType) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int getState() {
+        return state;
+    }
+
+    public String getSymbolicName() {
+        return location;
+    }
+
+    public Version getVersion() {
+        return new Version("0");
+    }
+
+    public boolean hasPermission(Object permission) {
+        return true;
+    }
+
+    public void start() throws BundleException {
+        state = Bundle.ACTIVE;
+    }
+
+    public void start(int options) throws BundleException {
+        state = Bundle.ACTIVE;
+    }
+
+    public void stop() throws BundleException {
+        state = Bundle.RESOLVED;
+    }
+
+    public void stop(int options) throws BundleException {
+        state = Bundle.RESOLVED;
+    }
+
+    public void uninstall() throws BundleException {
+        state = Bundle.UNINSTALLED;
+    }
+
+    public void update() throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void update(InputStream in) throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz,
+            String filter) throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Bundle getBundle() {
+        return this;
+    }
+
+    public Bundle getBundle(long id) {
+        return fw.getBundle(id);
+    }
+
+    public Bundle[] getBundles() {
+        return fw.getBundles();
+    }
+
+    public File getDataFile(String filename) {
+        return null;
+    }
+
+    public String getProperty(String key) {
+        return null;
+    }
+
+    public Object getService(ServiceReference reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter)
+            throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        return fw.installBundle(location);
+    }
+
+    public Bundle installBundle(String location, InputStream input)
+            throws BundleException {
+        return fw.installBundle(location, input);
+    }
+
+    public ServiceRegistration registerService(String[] clazzes,
+            Object service, Dictionary properties) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service,
+            Dictionary properties) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String toString() {
+        return id + " " + location;
+    }
+}

Added: projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/MiniFramework.java
===================================================================
--- projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/MiniFramework.java	                        (rev 0)
+++ projects/jboss-osgi/projects/aQute/trunk/runtime/src/main/java/aQute/junit/runtime/minifw/MiniFramework.java	2009-09-28 13:04:06 UTC (rev 94060)
@@ -0,0 +1,252 @@
+package aQute.junit.runtime.minifw;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import org.osgi.framework.*;
+import org.osgi.framework.launch.*;
+
+public class MiniFramework implements Framework, Bundle, BundleContext {
+    ClassLoader loader;
+    Map         properties;
+    Map         bundles = new HashMap();
+    int         ID      = 1;
+    ClassLoader last;
+    public MiniFramework(Map properties) {
+        this.properties = properties;
+        bundles.put(new Long(0), this);
+        last = loader = getClass().getClassLoader();
+    }
+
+    public void init() throws BundleException {
+        
+    }
+
+    public FrameworkEvent waitForStop(long timeout) throws InterruptedException {
+        return null;
+    }
+
+    public BundleContext getBundleContext() {
+        return this;
+    }
+
+    public long getBundleId() {
+        return 0;
+    }
+
+    public URL getEntry(String path) {
+        return loader.getResource(path);
+    }
+
+    public Enumeration getEntryPaths(String path) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Dictionary getHeaders() {
+        return new Hashtable();
+    }
+
+    public Dictionary getHeaders(String locale) {
+        throw new UnsupportedOperationException();
+    }
+
+    public long getLastModified() {
+        return 0;
+    }
+
+    public String getLocation() {
+        return "System Bundle";
+    }
+
+    public URL getResource(String name) {
+        return loader.getResource(name);
+    }
+
+    public Enumeration getResources(String name) throws IOException {
+        return loader.getResources(name);
+    }
+
+    public int getState() {
+        return Bundle.ACTIVE;
+    }
+
+    public String getSymbolicName() {
+        return "system.bundle";
+    }
+
+    public Version getVersion() {
+        return new Version("1.0");
+    }
+
+    public boolean hasPermission(Object permission) {
+        return true;
+    }
+
+    public Class loadClass(String name) throws ClassNotFoundException {
+        return loader.loadClass(name);
+    }
+
+    public void start() {
+    }
+
+    public void start(int options) {
+    }
+
+    public void stop() {
+    }
+
+    public void stop(int options) throws BundleException {
+    }
+
+    public Bundle getBundle() {
+        return this;
+    }
+
+    public Bundle getBundle(long id) {
+        Long l = new Long(id);
+        Bundle b = (Bundle) bundles.get(l);
+        return b;
+    }
+
+    public Bundle[] getBundles() {
+        Bundle[] bs = new Bundle[bundles.size()];
+        return (Bundle[]) bundles.values().toArray(bs);
+    }
+
+    public File getDataFile(String filename) {
+        return null;
+    }
+
+    public String getProperty(String key) {
+        return null;
+    }
+
+    public Bundle installBundle(String location) throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Bundle installBundle(String location, InputStream in)
+            throws BundleException {
+        Context c;
+        try {
+            c = new Context(this, last, ++ID, location);
+            bundles.put(new Long(c.id), c);
+            last = c;
+            return c;
+        } catch (IOException e) {
+            throw new BundleException("Can't install " + location, e);
+        }
+    }
+
+    public Enumeration findEntries(String path, String filePattern,
+            boolean recurse) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getRegisteredServices() {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getServicesInUse() {
+        throw new UnsupportedOperationException();
+    }
+
+    public Map getSignerCertificates(int signersType) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void uninstall() throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void update() throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void update(InputStream in) throws BundleException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addBundleListener(BundleListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addFrameworkListener(FrameworkListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addServiceListener(ServiceListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void addServiceListener(ServiceListener listener, String filter) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Filter createFilter(String filter) throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getAllServiceReferences(String clazz,
+            String filter) throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object getService(ServiceReference reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference getServiceReference(String clazz) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceReference[] getServiceReferences(String clazz, String filter)
+            throws InvalidSyntaxException {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceRegistration registerService(String[] clazzes,
+            Object service, Dictionary properties) {
+        throw new UnsupportedOperationException();
+    }
+
+    public ServiceRegistration registerService(String clazz, Object service,
+            Dictionary properties) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeBundleListener(BundleListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeFrameworkListener(FrameworkListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void removeServiceListener(ServiceListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean ungetService(ServiceReference reference) {
+        throw new UnsupportedOperationException();
+    }
+
+    public String toString() {
+        return "Mini framework";
+    }
+
+    class Loader extends ClassLoader {
+        public Class findClass(String name) throws ClassNotFoundException {
+            for (Iterator i = bundles.values().iterator(); i.hasNext();) {
+                Bundle b = (Bundle) i;
+                try {
+                    return b.loadClass(name);
+                } catch (ClassNotFoundException e) {
+                    // Ignore, try next
+                }
+            }
+            throw new ClassNotFoundException(name);
+        }
+    }
+}



More information about the jboss-osgi-commits mailing list