Initial commit of Sigil contribution. (FELIX-1142)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@793581 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/LICENCE b/sigil/LICENCE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sigil/LICENCE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/sigil/bld-ivy/.classpath b/sigil/bld-ivy/.classpath
new file mode 100644
index 0000000..f03d6f0
--- /dev/null
+++ b/sigil/bld-ivy/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/org.cauldron.bld.core"/>
+	<classpathentry kind="lib" path="lib/runtime/equinox.common.jar"/>
+	<classpathentry kind="lib" path="lib/runtime/osgi.core.jar"/>
+	<classpathentry kind="lib" path="lib/compile/ant.jar"/>
+	<classpathentry kind="lib" path="lib/compile/ivy-2.0.0-rc1.jar"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/sigil/bld-ivy/.project b/sigil/bld-ivy/.project
new file mode 100644
index 0000000..4ccb600
--- /dev/null
+++ b/sigil/bld-ivy/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bld-ivy</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/bld-ivy/build.xml b/sigil/bld-ivy/build.xml
new file mode 100644
index 0000000..b9f9dbf
--- /dev/null
+++ b/sigil/bld-ivy/build.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns:ivy="antlib:org.apache.ivy.ant" name="bld-ivy" default="jar">
+
+    <!-- buildVersion is overridden by Hudson -->
+    <property name="buildVersion" value="0.8.0-dev" />
+    <property name="version" value="${buildVersion}" />
+
+    <property name="name" value="sigil-ivy-plugin" />
+    <property name="name.jar" value="${name}.jar" />
+    <property name="name-version.jar" value="${name}-${version}.jar" />
+
+    <property name="lib.dir" value="${basedir}/lib" />
+    <property name="target.dir" value="${basedir}/target" />
+    <property name="classes.dir" value="${basedir}/target/classes" />
+
+    <property name="java-src.dir" value="${basedir}/src" />
+
+    <property name="sigil-plugins.dir" location="../sigil-builder/target/tmp/eclipse/plugins" />
+    <property name="bnd-plugins.dir" location="../org.cauldron.bld.core/lib" />
+
+    <target name="init" depends="find-plugins">
+
+      <path id="run.classpath">
+        <pathelement location="${classes.dir}" />
+        <pathelement location="${bld-core.jar}" />
+        <pathelement location="${bld-obr.jar}" />
+        <pathelement location="${equinox-common.jar}" />
+        <pathelement location="${osgi-core.jar}" />
+      </path>
+
+      <path id="compile.classpath">
+	<path refid="run.classpath"/>
+        <fileset dir="${lib.dir}/compile">
+            <include name="*.jar" />
+        </fileset>
+      </path>
+
+    </target>
+
+    <target name="mkdirs">
+        <mkdir dir="${target.dir}" />
+        <mkdir dir="${classes.dir}" />
+    </target>
+
+    <target name="find-plugins" depends="mkdirs">
+      <property name="osgi-core.jar" value="${lib.dir}/runtime/osgi.core.jar"/>
+      <property name="equinox-common.jar" value="${lib.dir}/runtime/equinox.common.jar"/>
+
+      <fileset id="bld-core" dir="${sigil-plugins.dir}"
+	includes="org.cauldron.bld.core_*.jar" />
+      <property name="bld-core" refid="bld-core"/>
+      <property name="bld-core.jar" location="${sigil-plugins.dir}/${bld-core}"/>
+
+      <fileset id="bld-obr" dir="${sigil-plugins.dir}"
+	includes="org.cauldron.bld.obr*.jar" />
+      <property name="bld-obr" refid="bld-obr"/>
+      <property name="bld-obr.jar" location="${sigil-plugins.dir}/${bld-obr}"/>
+    </target>
+
+    <target name="clean">
+        <delete dir="${target.dir}" />
+    </target>
+    
+    <target name="compile" depends="init">
+        <javac debug="true" debuglevel="source,lines,vars" source="1.5" target="1.5" fork="true" destdir="${classes.dir}" srcdir="${java-src.dir}">
+            <classpath refid="compile.classpath" />
+        </javac>
+    </target>
+    
+    <target name="dist" depends="jar">
+	<zip destfile="${target.dir}/sigil-${version}.zip">
+	  <zipfileset dir="example" prefix="sigil-${version}/example"
+	  	excludes="**/build/**, **/lib/**, **/ivy-cache/**, **/repository/**"/>
+	  <zipfileset dir="target" prefix="sigil-${version}/lib"
+		includes="${name.jar}, bndlib.jar"/>
+	</zip>
+    
+    	<copy file="${target.dir}/${name.jar}" tofile="${target.dir}/${name-version.jar}"/>
+    </target>
+
+    <target name="jar" depends="compile, taskdefs">
+       <bnd 
+	  classpath="${toString:run.classpath}" 
+	  files="sigil-ivy-plugin.bnd" 
+	  output="${target.dir}/${name.jar}"
+	  eclipse="false" 
+	  failok="false" 
+	  exceptions="true" />
+
+      <copy todir="${target.dir}">
+        <fileset dir="${bnd-plugins.dir}" includes="bndlib.jar"/>
+      </copy>
+    </target>
+
+    <target name="taskdefs"> 
+      <taskdef resource="aQute/bnd/ant/taskdef.properties"
+	classpath="${lib.dir}/ant/bnd-0.0.312.jar"/> 
+    </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/dependence/README b/sigil/bld-ivy/example/dependence/README
new file mode 100644
index 0000000..09ecfa8
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/README
@@ -0,0 +1,19 @@
+# http://sigil.codecauldron.org
+
+This example is based on the Ivy Tutorial project dependencies example:
+http://ant.apache.org/ivy/history/latest-milestone/tutorial/dependence.html
+
+Dependencies are resolved using an OBR index for the Spring repository,
+hosted at sigil.codecauldron.org.
+
+1. set ivy.jar location in settings/build.properties
+
+2. build dependee
+$ cd dependee
+$ ant jar
+$ ant publish
+
+3. build depender
+$ cd ../depender
+$ ant
+
diff --git a/sigil/bld-ivy/example/dependence/build.xml b/sigil/bld-ivy/example/dependence/build.xml
new file mode 100644
index 0000000..ccc460d
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/build.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project default="clean">
+    
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean directories">
+        <delete includeemptydirs="true"> 
+            <fileset dir="settings">
+		<exclude name="ivysettings.*" />
+		<exclude name="sigil-repos.properties" />
+            </fileset>
+	</delete>
+        <ant dir="dependee" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+    </target>
+
+    <!-- ================================= 
+          target: all              
+         ================================= -->
+    <target name="all" depends="clean" description="--> make the whole example of dependency">
+        <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+        <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+    </target>
+    
+</project>
diff --git a/sigil/bld-ivy/example/dependence/dependee/build.xml b/sigil/bld-ivy/example/dependence/dependee/build.xml
new file mode 100644
index 0000000..4eab164
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/build.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="dependee" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+    <!-- some variables used -->
+    <property name="lib.dir" value="${basedir}/lib" />
+    <property name="build.dir" value="${basedir}/build" />
+    <property name="classes.dir" value="${build.dir}/classes" />
+    <property name="src.dir" value="${basedir}/src" />
+
+    <!-- ivy properties used -->
+    <property name="ivy.settings.dir" value="../settings" />
+    <property file="${ivy.settings.dir}/ivysettings.properties" />
+    
+    <!-- paths used for compilation and run  -->
+    <path id="lib.path.id">
+        <fileset dir="${lib.dir}" />
+	</path>
+    <path id="run.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+    
+    <!-- ================================= 
+          target: init
+         ================================= -->
+    <target name="init">
+	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+		 uri="antlib:org.apache.ivy.ant"
+		 classpath="${ivy.jar}"/>
+
+	<ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+        <taskdef name="sigil.bundle"
+          classname="org.cauldron.bld.ant.BundleTask"
+          classpath="${sigil-ivy-plugin.jar}"/>
+    </target>
+
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="init"
+    	description="--> resolve and retrieve dependencies with ivy">
+        <ivy:retrieve />
+    </target>
+        
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+	
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> description">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="standalone.Main"/>
+    </target>
+    
+    <!-- ================================= 
+          target: jar              
+         ================================= -->
+    <target name="jar" depends="compile" description="--> make a jar file for this project">
+        <propertyfile file="${classes.dir}/version.properties">
+	        <entry  key="version" type="int" operation="+" default="0" />
+		</propertyfile>
+        <property file="${classes.dir}/version.properties" />
+	<!--
+        <jar destfile="${build.dir}/${ant.project.name}.jar">
+            <fileset dir="${classes.dir}" />
+        </jar>
+	-->
+	<sigil.bundle destpattern="${build.dir}/[id].[ext]"
+		      classpathref="run.path.id" />
+    </target>
+
+    <!-- ================================= 
+          target: publish              
+         ================================= -->
+    <target name="publish" depends="jar" description="--> publish this project in the ivy repository">
+        <property name="revision" value="${version}"/>
+        <delete file="${build.dir}/ivy.xml"/>
+		<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+			resolver="projects"
+			pubrevision="${revision}" 
+			status="release"
+		/>
+        <echo message="project ${ant.project.name} released with version ${revision}" />
+    </target>
+
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean the project">
+        <delete includeemptydirs="true">
+            <fileset dir="${basedir}">
+            	<exclude name="src/**" />
+            	<exclude name="build.xml" />
+		<exclude name="ivy.xml" />
+		<exclude name="sigil.properties" />
+	    </fileset>
+    	</delete>
+    </target>
+</project>
diff --git a/sigil/bld-ivy/example/dependence/dependee/ivy.xml b/sigil/bld-ivy/example/dependence/dependee/ivy.xml
new file mode 100644
index 0000000..631338a
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info organisation="org.apache" module="dependee"/>
+    <dependencies>
+        <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/dependence/dependee/sigil.properties b/sigil/bld-ivy/example/dependence/dependee/sigil.properties
new file mode 100644
index 0000000..619a841
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/sigil.properties
@@ -0,0 +1,24 @@
+# dependee sigil.properties
+
+version: 1.0.0
+
+-bundles: dependee
+
+-exports: standalone
+
+-imports: \
+  org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+# add these to -imports to test the resolution process
+#  javax.servlet;version="(2.4,3.0]", \
+#  org.apache.log4j;version="[1.2.14,1.3)", \
+#  org.apache.commons.logging, \
+
+# this is a require-bundle dependency
+#-requires: \
+#  com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+-resources: \
+  version.properties
+
+# end
diff --git a/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java b/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java
new file mode 100644
index 0000000..1b7398c
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package standalone;
+
+import java.util.Properties;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+    /**
+     * Returns the version of the project
+     * @return a string representation of the version, null if the version could not be retreived
+     */
+    public static String getVersion() {
+        Properties p = new Properties();
+        try {
+            p.load(Main.class.getResourceAsStream("/version.properties"));
+            String version = p.getProperty("version");
+            if (version != null) {
+                return String.valueOf(Integer.parseInt(version));
+            } 
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    
+    /**
+     * Return the same string with all words capitalized.
+     * @param str the string conatining the words to capitalize
+     * @return null if the string was null, the string with all words capitalized otherwise
+     */
+    public static String capitalizeWords(String str) {
+        System.out.println("    [" + Main.class.getName() + "] capitalizing string \"" 
+            + str + "\" using " + WordUtils.class.getName());
+        return WordUtils.capitalizeFully(str);
+    }
+    public static void main(String[] args) {
+        String message = "sentence to capitalize";
+        System.out.println("standard message : " + message);
+        System.out.println("capitalized message : " + capitalizeWords(message));
+    }
+    
+    private Main() {
+    }
+}
diff --git a/sigil/bld-ivy/example/dependence/depender/build.xml b/sigil/bld-ivy/example/dependence/depender/build.xml
new file mode 100644
index 0000000..f2f9190
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/build.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="depender" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+    <!-- some variables used -->
+    <property name="lib.dir" value="${basedir}/lib" />
+    <property name="build.dir" value="${basedir}/build" />
+    <property name="classes.dir" value="${build.dir}/classes" />
+    <property name="src.dir" value="${basedir}/src" />
+
+    <!-- ivy properties used -->
+    <property name="ivy.settings.dir" value="../settings" />
+    <property file="${ivy.settings.dir}/ivysettings.properties" />
+    
+    <!-- paths used for compilation and run  -->
+    <path id="lib.path.id">
+        <fileset dir="${lib.dir}" />
+	</path>
+    <path id="run.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+
+    <!-- ================================= 
+          target: init
+         ================================= -->
+    <target name="init">
+	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+		 uri="antlib:org.apache.ivy.ant"
+		 classpath="${ivy.jar}"/>
+
+	<ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+        <taskdef name="sigil.bundle"
+          classname="org.cauldron.bld.ant.BundleTask"
+          classpath="${sigil-ivy-plugin.jar}"/>
+    </target>
+
+    
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="init"
+    	description="--> resolve and retrieve dependencies with ivy">
+        <ivy:retrieve />
+    </target>
+    
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}" dot="true"/>
+    </target>
+    
+    <!-- ================================= 
+          target: gen-graph
+         ================================= -->
+    <target name="gen-graph" depends="report" description="--> generates a graph of dependencies (requires dot in your path - see http://www.graphviz.org/)">
+    	<property name="dot.file" value="${build.dir}/apache-depending-default.dot" />
+    	<property name="ivygraph.output.file" value="${build.dir}/graph.png" />
+    	<exec executable="dot">
+    	    <arg line="-T png -o ${ivygraph.output.file} ${dot.file}" />
+    	</exec>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> description">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="clean, compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="depending.Main"/>
+    </target>
+    
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean the project">
+        <delete includeemptydirs="true">
+            <fileset dir="${basedir}">
+            	<exclude name="src/**" />
+            	<exclude name="build.xml" />
+		<exclude name="ivy.xml" />
+		<exclude name="sigil.properties" />
+	    </fileset>
+    	</delete>
+    </target>
+</project>
diff --git a/sigil/bld-ivy/example/dependence/depender/ivy.xml b/sigil/bld-ivy/example/dependence/depender/ivy.xml
new file mode 100644
index 0000000..d8031f3
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info organisation="org.apache" module="depender"/>
+    <dependencies>
+        <dependency name="dependee" rev="latest.integration" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/dependence/depender/sigil.properties b/sigil/bld-ivy/example/dependence/depender/sigil.properties
new file mode 100644
index 0000000..e76ec83
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/sigil.properties
@@ -0,0 +1,7 @@
+# depender sigil.properties
+
+-bundles: depender
+
+-imports: standalone
+
+# end
diff --git a/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java b/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java
new file mode 100644
index 0000000..59b0399
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package depending;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+    public static void main(String[] args) {
+        String standaloneVersion = standalone.Main.getVersion();
+        if (standaloneVersion != null) {
+            System.out.println("you are using version " + standaloneVersion 
+                + " of class " + standalone.Main.class.getName());
+        } else {
+            System.err.println("failed to get version of " + standalone.Main.class.getName());
+        }
+        String message = "i am " + Main.class.getName() 
+            + " and " + standalone.Main.class.getName() + " will do the job for me";
+        System.out.println("standard message : " + message);
+        System.out.println("capitalized message : " + standalone.Main.capitalizeWords(message));
+    }
+    
+    private Main() {
+    }
+}
diff --git a/sigil/bld-ivy/example/dependence/settings/ivysettings.properties b/sigil/bld-ivy/example/dependence/settings/ivysettings.properties
new file mode 100644
index 0000000..737733c
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/ivysettings.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+repository.dir=${ivy.settings.dir}/repository
+sigil-ivy-plugin.jar=${ivy.settings.dir}/../../../lib/sigil-ivy-plugin.jar
+ivy.jar=/opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
diff --git a/sigil/bld-ivy/example/dependence/settings/ivysettings.xml b/sigil/bld-ivy/example/dependence/settings/ivysettings.xml
new file mode 100644
index 0000000..5dd4e63
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/ivysettings.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+    <properties file="${ivy.settings.dir}/ivysettings.properties"/>
+    <settings defaultResolver="projects"/>
+    <caches defaultCacheDir="${ivy.settings.dir}/ivy-cache" />
+
+    <classpath file="${sigil-ivy-plugin.jar}" />
+    <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+    <typedef name="sigil-resolver" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+    <parsers>
+	<sigil-parser/>
+    </parsers>
+
+    <resolvers>
+	<sigil-resolver name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+        <filesystem name="projects">
+            <artifact pattern="${repository.dir}/[artifact]-[revision].[ext]" />
+            <ivy pattern="${repository.dir}/[module]-[revision].xml" />
+        </filesystem>
+        <!--
+        <ibiblio name="libraries" m2compatible="true" usepoms="false" />
+        -->
+    </resolvers>
+    <modules>
+        <module organisation="sigil" name="*" resolver="sigil"/>
+    </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties b/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties
new file mode 100644
index 0000000..85150e2
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties
@@ -0,0 +1,17 @@
+# repository config
+
+-repositories:	system, project, spring
+
+system;provider:	system
+system;level:		-1
+
+project;provider:	project
+project;level:	0
+project;pattern:	../*/[sigilproject]
+
+spring;provider:	obr
+spring;level:		2
+spring;url:		http://sigil.codecauldron.org/spring-external.obr
+spring;index:		../settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/example/felix-log/README b/sigil/bld-ivy/example/felix-log/README
new file mode 100644
index 0000000..df71d9b
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/README
@@ -0,0 +1,22 @@
+# http://sigil.codecauldron.org
+
+This example shows how to build the felix log service, using Ant/Ivy,
+instead of Maven.
+
+sigil.properties has been constructed to produce an identical bundle to the
+one produced by Maven.
+
+The Felix dependencies are obtained from an OBR index of
+http://repo1.maven.org/maven2/org/apache/felix, Maven repos can't be searched
+directly for package import dependencies.
+
+1. checkout felix log code
+  $ cd felix-log
+  $ svn co http://svn.apache.org/repos/asf/felix/trunk/log
+
+2. set ivy.jar location in common/build.properties
+
+3. build
+  $ cd log
+  $ ant
+
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/build.properties b/sigil/bld-ivy/example/felix-log/bldcommon/build.properties
new file mode 100644
index 0000000..ff1bcc1
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/build.properties
@@ -0,0 +1,26 @@
+# common properties
+
+# CHANGE ivy.jar TO REFLECT YOUR INSTALLATION
+ivy.jar = /opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
+
+sigil-ivy-plugin.jar = ${common.dir}/../../../lib/sigil-ivy-plugin.jar
+
+build.dir	= ${basedir}/build
+classes.dir	= ${build.dir}/classes
+deps.dir	= ${build.dir}/deps
+resource.dir	= ${basedir}/xml
+src.dir		= ${basedir}/src
+
+dest.dir	= ${build.dir}
+dest.lib.dir	= ${dest.dir}/lib
+dest.etc.dir	= ${dest.dir}/etc
+
+ivy.file	= ${basedir}/ivy.xml
+module.version.target = 1.0
+
+cache.dir	= ${common.dir}/ivy-cache
+repository.dir	= ${common.dir}/repository
+
+example.version	= 1.0.0
+bundle.version	= ${example.version}
+
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/common.xml b/sigil/bld-ivy/example/felix-log/bldcommon/common.xml
new file mode 100644
index 0000000..8431484
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/common.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="common" 
+         xmlns:ivy="antlib:org.apache.ivy.ant">
+    
+    <dirname property="common.dir" file="${ant.file.common}"/>
+    <property file="${common.dir}/build.properties"/>
+
+    <path id="lib.path.id">
+        <fileset dir="${deps.dir}" />
+    </path>
+
+    <path id="sigil.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+
+    <!-- setup ivy default configuration with some custom info -->
+    <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+    <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+    <!-- ================================= 
+          target: load-ivy
+         ================================= -->
+    <target name="load-ivy" depends="ivy-taskdefs">
+	<ivy:settings file="${common.dir}/ivysettings.xml" />
+    </target>
+
+    <!-- ================================= 
+          target: ivy-taskdefs
+         ================================= -->
+    <target name="ivy-taskdefs" unless="ivy.loaded">
+    	<property name="ivy.loaded" value="true"/>
+	<echo message="Loading Ivy ... common.dir=${common.dir}"/>
+
+    	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+    	         uri="antlib:org.apache.ivy.ant"
+		 classpath="${ivy.jar}"/>
+
+	<taskdef name="sigil.bundle"
+		 classname="org.cauldron.bld.ant.BundleTask"
+		 classpath="${sigil-ivy-plugin.jar}"/>
+    </target>    
+
+
+
+    <!-- ================================= 
+          target: build (default target)
+         ================================= -->
+    <target name="build" depends="ident, jar, resources" />
+
+    <target name="ident">
+      <echo message="${ant.project.name}"/>
+    </target>
+
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="load-ivy, clean-deps"
+    	description="--> resolve and retrieve dependencies with ivy">
+	<mkdir dir="${deps.dir}"/>
+    	<ivy:resolve file="${ivy.file}"/>
+    	<ivy:retrieve pattern="${deps.dir}/[artifact].[ext]" />
+    </target>
+
+    <!-- ================================= 
+          target: install
+         ================================= -->
+    <target name="install" depends="install-version, build"
+    	description="--> publish this project in the local repository">
+    	<ivy:publish artifactspattern="${dest.lib.dir}/[artifact].[ext]" 
+		       resolver="local"
+		       pubrevision="${version}" 
+		       pubdate="${now}"
+		       forcedeliver="true"
+		       status="integration"/>
+        <echo message="project ${ant.project.name} published locally with version ${version}" />
+    </target>
+
+    <target name="install-version">
+	<tstamp>
+	    <format property="now" pattern="yyyyMMddHHmmss"/>
+	</tstamp>
+        <property name="version"
+	      value="${module.version.target}-local-${now}"/>
+    </target>
+    
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve"
+    	description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve"
+    	description="--> compile the project">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}"
+		destdir="${classes.dir}"
+		classpathref="lib.path.id"
+		target="1.5"
+		debug="true" />
+    </target>
+    
+    <!-- ================================= 
+          target: jar              
+         ================================= -->
+    <target name="jar" depends="compile"
+    	description="--> make a jar file for this project">
+	<mkdir dir="${dest.lib.dir}"/>
+        <sigil.bundle destpattern="${dest.lib.dir}/[id].[ext]"
+                classpathref="sigil.path.id"/>
+    </target>
+
+    <!-- ================================= 
+          target: resources              
+         ================================= -->
+    <available file="${resource.dir}" type="dir"
+               property="resource.dir.present"/>
+
+    <target name="resources" if="resource.dir.present"
+    	description="--> filter xml resources replacing ${VERSION} etc.">
+	<mkdir dir="${dest.etc.dir}"/>
+	<copy todir="${dest.etc.dir}">
+	  <fileset dir="${resource.dir}">
+	    <include name="*.composite"/>
+	    <include name="*.system"/>
+	  </fileset>
+	  <filterset begintoken="$${" endtoken="}">
+	    <filter token="VERSION" value="${bundle.version}"/>
+	    <filter token="BLITZ_VERSION" value="${blitz.version}"/>
+	    <filter token="JINI_VERSION" value="${jini.version}"/>
+	  </filterset>
+	</copy>
+    </target>
+
+    <!-- ================================= 
+      target: clean-local              
+     ================================= -->
+    <target name="clean-local"
+    	description="--> cleans the local repository for the current module">
+       <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+    </target>
+
+    <!-- ================================= 
+      target: clean-deps              
+     ================================= -->
+    <target name="clean-deps"
+    	description="--> clean the project libraries directory (dependencies)">
+	<delete includeemptydirs="true" dir="${deps.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean-build              
+         ================================= -->
+    <target name="clean-build"
+    	description="--> clean the project built files">
+        <delete includeemptydirs="true" dir="${build.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" depends="clean-build, clean-deps"
+    	description="--> clean the project" />
+
+
+    <!-- ================================= 
+          target: buildlist
+         ================================= -->
+    <fileset id="projects" dir="${basedir}" includes="**/build.xml"/>
+
+    <target name="buildlist" depends="load-ivy">
+      <ivy:buildlist reference="ordered-list"
+		  onMissingDescriptor="skip">
+	<fileset refid="projects"/>
+      </ivy:buildlist>
+    </target>
+
+    <!-- ================================= 
+          target: install-list
+         ================================= -->
+    <target name="install-list" depends="buildlist" 
+	  description="--> compile, jar and install all projects in the right order">
+      <subant target="install" buildpathref="ordered-list">
+	<propertyset>
+	  <propertyref name="ivy.loaded" />
+	</propertyset>
+      </subant>
+    </target>
+
+    <!-- ================================= 
+          target: clean-list
+         ================================= -->
+    <target name="clean-list" depends="clean, buildlist"
+	  description="--> clean all projects">
+      <subant target="clean" buildpathref="ordered-list" />
+    </target>
+
+    <!-- ================================= 
+          target: clean-all
+         ================================= -->
+    <target name="clean-all" depends="clean-list" 
+    description="--> delete repository, ivy cache, and all projects">
+      <delete dir="${repository.dir}"/>
+      <ivy:cleancache />
+    </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml b/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml
new file mode 100644
index 0000000..dd336c7
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+  <properties file="${ivy.settings.dir}/build.properties"/>
+  <caches defaultCacheDir="${cache.dir}" />
+
+  <settings defaultResolver="local" circularDependencyStrategy="error" />
+
+  <classpath file="${sigil-ivy-plugin.jar}" />
+  <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+  <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+  <parsers>
+    <sigil-parser config="${ivy.settings.dir}/sigil-repos.properties"
+    		quiet="true"/>
+  </parsers>
+
+  <resolvers>
+    <sigil name="sigil"
+	   config="${ivy.settings.dir}/sigil-repos.properties"
+	   extractBCP="true"/>
+
+    <filesystem name="local">
+      <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+    </filesystem>
+
+    <filesystem name="shared">
+      <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+    </filesystem>
+  </resolvers>
+
+  <modules>
+    <module organisation="sigil" resolver="sigil"/>
+  </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties b/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties
new file mode 100644
index 0000000..fbc95c6
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties
@@ -0,0 +1,17 @@
+# sigil repository config
+
+-repositories:	system, project, felix
+
+system;provider:	system
+system;level:		-1
+
+project;provider:	project
+project;level:		0
+project;pattern:	../*/[sigilproject]
+
+felix;provider:         obr
+felix;level:            1
+felix;url:              http://sigil.codecauldron.org/maven-felix.obr
+felix;index:            ../bldcommon/maven-felix.obr
+
+# end
diff --git a/sigil/bld-ivy/example/felix-log/log/build.xml b/sigil/bld-ivy/example/felix-log/log/build.xml
new file mode 100644
index 0000000..c7c19f6
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/build.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="felix-log" default="build">
+      <import file="../bldcommon/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/felix-log/log/ivy.xml b/sigil/bld-ivy/example/felix-log/log/ivy.xml
new file mode 100644
index 0000000..6e0b4db
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.felix"
+        module="felix-log"
+        status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/felix-log/log/sigil.properties b/sigil/bld-ivy/example/felix-log/log/sigil.properties
new file mode 100644
index 0000000..3443218
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/sigil.properties
@@ -0,0 +1,38 @@
+# sigil project file, saved by plugin.
+
+-activator: ${Bundle-SymbolicName}.impl.Activator
+
+name: org.apache.felix.log
+
+version: 0.9.0.SNAPSHOT
+
+-bundles: \
+	felix-log, \
+
+-contents: \
+	${Bundle-SymbolicName}.impl, \
+	org.osgi.service.log, \
+
+-resources: \
+	=src/main/resources, \
+
+-sourcedirs: \
+	src/main/java, \
+
+-exports: \
+	org.osgi.service.log, \
+
+-imports: \
+	org.osgi.framework;version=1.4, \
+	org.osgi.service.log;version=1.3, \
+
+header;Bundle-Vendor: The Apache Software Foundation
+
+header;Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+
+header;Export-Service: org.osgi.service.log.LogService,org.osgi.service.log.LogReaderService
+
+header;Bundle-Name: Apache Felix Log Service
+
+header;Bundle-DocURL: http://www.apache.org/
+
diff --git a/sigil/bld-ivy/example/multi-project/README b/sigil/bld-ivy/example/multi-project/README
new file mode 100644
index 0000000..ed39dc3
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/README
@@ -0,0 +1,19 @@
+# http://sigil.codecauldron.org
+
+This example is based on the Ivy Tutorial multi-project example:
+http://ant.apache.org/ivy/history/latest-milestone/tutorial/multiproject.html
+
+Dependencies are resolved using an OBR index for the Spring repository,
+hosted at sigil.codecauldron.org.
+
+projects/sigil-defaults.properties specifies default package version ranges,
+which are inherited by the -imports in sigil.properties.
+
+1. set ivy.jar location in common/build.properties
+
+2. test build order (determined automatically from the dependencies)
+$ ant order
+
+3. build
+$ ant publish
+
diff --git a/sigil/bld-ivy/example/multi-project/build.xml b/sigil/bld-ivy/example/multi-project/build.xml
new file mode 100644
index 0000000..a04ef74
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/build.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="all" default="publish" xmlns:ivy="antlib:org.apache.ivy.ant">
+  <property name="common.dir" value="${basedir}/common" />
+  <import file="${common.dir}/common.xml"/>
+
+  <fileset id="projects" dir="projects" includes="**/build.xml"/>
+
+  <path id="unordered-list">
+    <fileset refid="projects"/>
+  </path>
+
+  <target name="buildlist" depends="load-ivy"> 
+    <ivy:buildlist reference="ordered-list">
+      <fileset refid="projects"/>
+    </ivy:buildlist>
+  </target>
+  
+  <target name="publish" depends="buildlist" 
+	description="compile, jar and publish all projects in the right order">
+    <subant target="publish" buildpathref="ordered-list">
+      <propertyset>
+	<propertyref name="ivy.loaded" />
+      </propertyset>
+    </subant>
+  </target>
+
+  <target name="clean" description="clean all projects">
+    <subant target="clean" buildpathref="unordered-list" />
+  </target>
+  
+  <target name="fullclean" depends="clean, load-ivy" 
+  description="clean tutorial: delete repository, ivy cache, and all projects">
+    <delete dir="repository"/>
+  	<ivy:cleancache />
+  </target>
+
+  <target name="order" depends="buildlist"
+      description="test buildlist order">
+    <subant target="hello" buildpathref="ordered-list" />
+  </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/common/build.properties b/sigil/bld-ivy/example/multi-project/common/build.properties
new file mode 100644
index 0000000..79298b4
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/build.properties
@@ -0,0 +1,36 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+
+# CHANGE ivy.jar TO REFLECT YOUR INSTALLATION
+ivy.jar = /opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
+
+sigil-ivy-plugin.jar = ${common.dir}/../../../lib/sigil-ivy-plugin.jar
+
+lib.dir = ${basedir}/lib
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+src.dir = ${basedir}/src
+repository.dir=${common.dir}/../repository
+
+ivy.file = ${basedir}/ivy.xml
+
+jar.file = ${build.dir}/${ant.project.name}.jar
+main.class.name = ${ant.project.name}.Main
+
+module.version.target = 1.0
diff --git a/sigil/bld-ivy/example/multi-project/common/common.xml b/sigil/bld-ivy/example/multi-project/common/common.xml
new file mode 100644
index 0000000..f464e32
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/common.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="common" 
+         xmlns:ivy="antlib:org.apache.ivy.ant">
+	<!-- a sample common ant build file, used for ivy multi-project tutorial
+	     feel free to copy and adapt it to your own needs
+	     Note that the only targets specific to ivy are:
+	        load-ivy
+	     	resolve
+	     	report
+	     	ivy-new-version
+	     	publish
+	     	publish-local
+	     	
+	     All other targets are usual ant based targets, which could have been written
+	     in a build not depending at all on ivy: 
+	     resolve constructs a lib directory based upon ivy dependencies, and then the lib dir 
+	     is used as in any classical ant build
+	     -->
+	
+	<property file="${common.dir}/build.properties"/>
+	
+	<!-- ================================= 
+          target: load-ivy         
+            this target is not necessary if you put ivy.jar in your ant lib directory
+            if you already have ivy 2.0 in your ant lib, you can simply remove this
+            target
+         ================================= -->
+
+    <target name="load-ivy" depends="ivy-taskdefs">
+	<ivy:settings file="${common.dir}/ivysettings.xml" />
+    </target>
+
+    <target name="ivy-taskdefs" unless="ivy.loaded">
+    	<property name="ivy.loaded" value="true"/>
+	<echo message="Loading Ivy ..."/>
+    	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+    	         uri="antlib:org.apache.ivy.ant"
+		 classpath="${ivy.jar}"/>
+
+	<taskdef name="sigil.bundle"
+		 classname="org.cauldron.bld.ant.BundleTask"
+		 classpath="${sigil-ivy-plugin.jar}"/>
+    </target>    
+
+    <path id="lib.path.id">
+        <fileset dir="${lib.dir}" />
+    </path>
+
+    <path id="run.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+
+    
+    <!-- setup ivy default configuration with some custom info -->
+    <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+    <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+    <!-- here is how we would have configured ivy if we had our own ivysettings file
+    <ivy:settings file="${common.dir}/ivysettings.xml" />
+    -->
+
+    <!-- ================================= 
+          target: hello              
+         ================================= -->
+    <target name="hello" description="--> identify project (useful to test buildlist)">
+	<echo message="${ant.project.name}"/>
+    </target>
+	
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="clean-lib, load-ivy" description="--> resolve and retrieve dependencies with ivy">
+        <mkdir dir="${lib.dir}"/> <!-- not usually necessary, ivy creates the directory IF there are dependencies -->
+    	
+    	<!-- the call to resolve is not mandatory, retrieve makes an implicit call if we don't -->
+    	<ivy:resolve file="${ivy.file}"/>
+    	<ivy:retrieve pattern="${lib.dir}/[artifact].[ext]" />
+    </target>
+    
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> compile the project">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" debug="true" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="version, compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="${main.class.name}"/>
+    </target>
+
+	<target name="ivy-new-version" depends="load-ivy" unless="ivy.new.revision">
+    	<!-- default module version prefix value -->
+		<property name="module.version.prefix" value="${module.version.target}-dev-b" />
+		
+    	<!-- asks to ivy an available version number -->
+		<ivy:info file="${ivy.file}" />
+    	<ivy:buildnumber 
+    		organisation="${ivy.organisation}" module="${ivy.module}" 
+    		revision="${module.version.prefix}" defaultBuildNumber="1" revSep=""/>
+	</target>
+
+    <target name="local-version">
+		<tstamp>
+			<format property="now" pattern="yyyyMMddHHmmss"/>
+		</tstamp>
+        <property name="ivy.new.revision" value="${module.version.target}-local-${now}"/>
+    </target>
+	
+	<target name="version" depends="ivy-new-version">
+    	<!-- create version file in classpath for later inclusion in jar -->
+        <mkdir dir="${classes.dir}"/>
+		<echo message="version=${ivy.new.revision}" file="${classes.dir}/${ant.project.name}.properties" append="false" />
+
+		<!-- load generated version properties file -->
+        <property file="${classes.dir}/${ant.project.name}.properties" />
+    </target>
+
+    <!-- ================================= 
+          target: jar              
+         ================================= -->
+    <target name="jar" depends="version, compile" description="--> make a jar file for this project">
+
+	<!--
+        <jar destfile="${jar.file}">
+            <fileset dir="${classes.dir}" />
+            <manifest>
+                <attribute name="Built-By" value="${user.name}"/>
+                <attribute name="Build-Version" value="${version}" />
+            </manifest>
+        </jar>
+	-->
+
+        <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+                classpathref="run.path.id"/>
+    </target>
+
+    <!-- ================================= 
+          target: publish              
+         ================================= -->
+    <target name="publish" depends="clean-build, jar" description="--> publish this project in the ivy repository">
+    	<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+    			           resolver="shared"
+    			           pubrevision="${version}" 
+    			           status="release"
+    	/>
+        <echo message="project ${ant.project.name} released with version ${version}" />
+    </target>
+
+    <!-- ================================= 
+          target: publish-local              
+         ================================= -->
+    <target name="publish-local" depends="local-version, jar" description="--> publish this project in the local ivy repository">
+    	<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+    			        resolver="local"
+    			        pubrevision="${version}"
+				        pubdate="${now}"
+    			        status="integration"
+    					forcedeliver="true"
+    	/>
+        <echo message="project ${ant.project.name} published locally with version ${version}" />
+    </target>
+
+	<!-- ================================= 
+          target: clean-local              
+         ================================= -->
+	<target name="clean-local" description="--> cleans the local repository for the current module">
+	   <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+	</target>
+
+	<!-- ================================= 
+          target: clean-lib              
+         ================================= -->
+    <target name="clean-lib" description="--> clean the project libraries directory (dependencies)">
+        <delete includeemptydirs="true" dir="${lib.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean-build              
+         ================================= -->
+    <target name="clean-build" description="--> clean the project built files">
+        <delete includeemptydirs="true" dir="${build.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" depends="clean-build, clean-lib" description="--> clean the project" />
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/common/ivysettings.xml b/sigil/bld-ivy/example/multi-project/common/ivysettings.xml
new file mode 100644
index 0000000..614aa0e
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/ivysettings.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+  <properties file="${ivy.settings.dir}/build.properties"/>
+  <caches defaultCacheDir="${ivy.settings.dir}/../ivy-cache" />
+
+  <settings defaultResolver="shared" circularDependencyStrategy="error" />
+
+  <classpath file="${ivy.settings.dir}/../../../lib/sigil-ivy-plugin.jar" />
+  <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+  <typedef name="sigil-resolver" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+  <parsers>
+    <sigil-parser quiet="true"/>
+  </parsers>
+
+  <resolvers>
+    <sigil-resolver name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+    <filesystem name="local">
+      <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+    </filesystem>
+    <filesystem name="shared">
+      <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+    </filesystem>
+  </resolvers>
+  <modules>
+    <!--<module organisation="org.apache.ivy.example" name=".*" resolver="shared"/>
+    -->
+    <module organisation="sigil" resolver="sigil"/>
+  </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties b/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties
new file mode 100644
index 0000000..bd711fa
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties
@@ -0,0 +1,20 @@
+
+# repository config
+
+-repositories:	system, project, spring
+
+system;provider:	system
+system;level:		-1
+#system;framework:	osgi.core.jar
+#system;profile:	J2SE-1.5
+
+project;provider:	project
+project;level:	0
+project;pattern:	../projects/**/[sigilproject]
+
+spring;provider:	obr
+spring;level:		2
+spring;url:		http://sigil.codecauldron.org/spring-external.obr
+spring;index:		../common/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/build.properties b/sigil/bld-ivy/example/multi-project/projects/console/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/build.xml b/sigil/bld-ivy/example/multi-project/projects/console/build.xml
new file mode 100644
index 0000000..5c02cba
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="console" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml
new file mode 100644
index 0000000..b1eb4f4
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="console"
+        status="integration"/>
+    <dependencies>
+      <dependency name="version" rev="latest.integration" conf="default" />
+      <dependency name="list" rev="latest.integration" conf="default->standalone" />
+      <dependency name="find" rev="latest.integration" conf="default->standalone" />
+      <dependency name="sizewhere" rev="latest.integration" conf="default->standalone" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties
new file mode 100644
index 0000000..dea089f
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties
@@ -0,0 +1,6 @@
+
+-bundles: console
+
+-exports: console
+
+-imports: list
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java b/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java
new file mode 100644
index 0000000..678f3ca
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package console;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Arrays;
+import java.lang.reflect.Method;
+
+
+public class Main {
+    private static Collection QUIT_COMMANDS = Arrays.asList(new String[] {"quit", "q", "exit"});
+    private static Collection HELP_COMMANDS = Arrays.asList(new String[] {"help", "h", "?"});
+
+    public static void main(String[] a) throws Exception {
+      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+      while (true) {
+        System.out.print("> ");
+        String command = in.readLine().trim();
+        if (QUIT_COMMANDS.contains(command)) {
+          return;
+        }
+        if (HELP_COMMANDS.contains(command)) {
+          help();
+          continue;
+        }
+        String[] split = command.split(" ");
+        if (split.length == 0) {
+          error(command);
+          continue;
+        }
+        
+        try {
+          String[] args = new String[split.length - 1];
+          System.arraycopy(split, 1, args, 0, args.length);
+          Class cl = Class.forName(split[0]+".Main");
+          Method m = cl.getMethod("main", new Class[] {String[].class});
+          m.invoke(null, new Object[] {args});
+        } catch (Exception ex) {
+          error(command);
+          continue;
+        }
+      }
+    }
+    
+    private static void help() {
+      System.out.println("available commands:");
+      System.out.println("\tquit: quit the console");
+      System.out.println("\thelp: displays this message");
+      System.out.println("\tlist -dir <dir>: list files in given directory");
+      System.out.println("\tfind -dir <dir> -name <name>: list files with given name in given dir");
+      System.out.println("\tsizewhere -dir <dir> -name <name>: compute total size of files with given name in given dir");
+      System.out.println("\thelp: displays this message");
+    }
+            
+    private static void error(String command) {
+      System.out.println("unknown command "+command);
+      System.out.println("type ? for help");
+    }
+            
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/build.properties b/sigil/bld-ivy/example/multi-project/projects/find/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/build.xml b/sigil/bld-ivy/example/multi-project/projects/find/build.xml
new file mode 100644
index 0000000..4d23bf5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="find" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml
new file mode 100644
index 0000000..b7f1bc1
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="find"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="find" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency name="list" rev="latest.integration" conf="core" />
+      <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="core->default" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties
new file mode 100644
index 0000000..30c10a7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: find
+
+-exports: find
+
+-imports:\
+  version, \
+  list, \
+  org.apache.commons.collections, \
+  org.apache.commons.cli
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java b/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java
new file mode 100644
index 0000000..25fa1af
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import version.Version;
+import list.ListFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+import  org.apache.commons.collections.CollectionUtils;
+import  org.apache.commons.collections.Predicate;
+
+public class FindFile {
+  static {
+    Version.register("find");
+  }
+  
+  public static Collection find(File dir, String name) {
+    return find(ListFile.list(dir), name);
+  }
+  
+  private static Collection find(Collection files, final String name) {    
+    return CollectionUtils.select(files, new Predicate() {
+      public boolean evaluate(Object o) {
+        return ((File)o).getName().indexOf(name) != -1;
+      }
+    });
+  }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java b/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java
new file mode 100644
index 0000000..a41c136
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName("dir")
+            .hasArg()
+            .withDescription("list files in given dir")
+            .create("dir");
+        Option name = OptionBuilder.withArgName("name")
+            .hasArg()
+            .withDescription("list files with given name")
+            .create("name");
+        Options options = new Options();
+
+        options.addOption(dir);
+        options.addOption(name);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+        Options options = getOptions();
+        try {
+        
+            CommandLineParser parser = new GnuParser();
+    
+            CommandLine line = parser.parse(options, args);
+            File dir = new File(line.getOptionValue("dir", "."));
+            String name = line.getOptionValue("name", "jar");
+            Collection files = FindFile.find(dir, name);
+            System.out.println("listing files in " + dir + " containing " + name);
+            for (Iterator it = files.iterator(); it.hasNext();) {
+                System.out.println("\t" + it.next() + "\n");
+            }
+        } catch(ParseException exp) {
+            // oops, something went wrong
+            System.err.println("Parsing failed.  Reason: " + exp.getMessage());
+              
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp("find", options);
+        }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/build.properties b/sigil/bld-ivy/example/multi-project/projects/list/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/build.xml b/sigil/bld-ivy/example/multi-project/projects/list/build.xml
new file mode 100644
index 0000000..3926148
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="list" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml
new file mode 100644
index 0000000..6593fb7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="list"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="list" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties
new file mode 100644
index 0000000..302cba7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties
@@ -0,0 +1,14 @@
+
+# requirements
+
+-imports:\
+  version, \
+  org.apache.commons.cli
+
+# exports
+
+-bundles: list
+
+list;name: org.example.list
+list;-exports: list.*
+
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java b/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java
new file mode 100644
index 0000000..7467152
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import version.Version;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class ListFile {
+  static {
+    Version.register("list");
+  }
+  
+  public static Collection list(File dir) {
+    Collection files = new ArrayList();
+    
+    return list(dir, files);
+  }
+  
+  private static Collection list(File file, Collection files) {
+    if (file.isDirectory()) {
+      File[] f = file.listFiles();
+      for (int i=0; i<f.length; i++) {
+        list(f[i], files);
+      }
+    } else {
+      files.add(file);
+    }
+    return files;
+  }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java b/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java
new file mode 100644
index 0000000..0f8cb14
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName( "dir" )
+            .hasArg()
+            .withDescription(  "list files in given dir" )
+            .create( "dir" );
+        Options options = new Options();
+
+        options.addOption(dir);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+      Options options = getOptions();
+      try {
+        
+        CommandLineParser parser = new GnuParser();
+
+        CommandLine line = parser.parse( options, args );
+        File dir = new File(line.getOptionValue("dir", "."));
+        Collection files = ListFile.list(dir);
+        System.out.println("listing files in "+dir);
+        for (Iterator it = files.iterator(); it.hasNext(); ) {
+          System.out.println("\t"+it.next()+"\n");
+        }
+      } catch( ParseException exp ) {
+          // oops, something went wrong
+          System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
+          
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp( "list", options );
+      }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties b/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties
new file mode 100644
index 0000000..d61df45
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties
@@ -0,0 +1,4 @@
+# sigil-defaults.properties
+
+package;org.apache.commons.cli: [1.1,1.2)
+package;org.apache.commons.collections: [3.0,4.0)
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/build.properties b/sigil/bld-ivy/example/multi-project/projects/size/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/build.xml b/sigil/bld-ivy/example/multi-project/projects/size/build.xml
new file mode 100644
index 0000000..2a75fe5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="size" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml
new file mode 100644
index 0000000..7112b7f
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="size"
+        status="integration"/>
+    <dependencies>
+      <dependency name="version" rev="latest.integration" conf="default" />
+      <dependency name="list" rev="latest.integration" conf="default->core" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties
new file mode 100644
index 0000000..cf239af
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties
@@ -0,0 +1,8 @@
+
+-bundles: size
+
+-exports: size
+
+-imports:\
+  list, \
+  version
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java b/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java
new file mode 100644
index 0000000..dcd4a1c
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package size;
+
+import version.Version;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+public class FileSize {
+  static {
+    Version.register("size");
+  }
+
+  public static long totalSize(File dir) {
+    return totalSize(list.ListFile.list(dir));
+  }
+  
+  public static long totalSize(Collection files) {
+    long total = 0;
+    for (Iterator it = files.iterator(); it.hasNext(); ) {
+      File f = (File)it.next();
+      total += f.length();
+    }
+    return total;
+  }  
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml
new file mode 100644
index 0000000..0b8ab4c
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="sizewhere" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml
new file mode 100644
index 0000000..d50bddb
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="sizewhere"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="sizewhere" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency name="size" rev="latest.integration" conf="core->default" />
+      <dependency name="find" rev="latest.integration" conf="core" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties
new file mode 100644
index 0000000..c272f9b
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: sizewhere
+
+-exports: sizewhere
+
+-imports:\
+  find, \
+  size, \
+  version, \
+  org.apache.commons.cli
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
new file mode 100644
index 0000000..131af66
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName( "dir" )
+            .hasArg()
+            .withDescription(  "give total size of files in given dir" )
+            .create( "dir" );
+        Option name = OptionBuilder.withArgName( "name" )
+            .hasArg()
+            .withDescription(  "give total size of files with given name" )
+            .create( "name" );
+        Options options = new Options();
+
+        options.addOption(dir);
+        options.addOption(name);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+      Options options = getOptions();
+      try {
+        
+        CommandLineParser parser = new GnuParser();
+
+        CommandLine line = parser.parse( options, args );
+        File dir = new File(line.getOptionValue("dir", "."));
+        String name = line.getOptionValue("name", "jar");
+        System.out.println("total size of files in "+dir+" containing "+name+": "+SizeWhere.totalSize(dir, name));
+      } catch( ParseException exp ) {
+          // oops, something went wrong
+          System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
+          
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp( "sizewhere", options );
+      }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
new file mode 100644
index 0000000..3594bd9
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import version.Version;
+import size.FileSize;
+import find.FindFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class SizeWhere {
+  static {
+    Version.register("sizewhere");
+  }
+  
+  public static long totalSize(File dir, String name) {
+    return FileSize.totalSize(FindFile.find(dir, name));
+  }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/build.properties b/sigil/bld-ivy/example/multi-project/projects/version/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/build.xml b/sigil/bld-ivy/example/multi-project/projects/version/build.xml
new file mode 100644
index 0000000..375db20
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="version" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml
new file mode 100644
index 0000000..16e08f3
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="version"
+        status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties
new file mode 100644
index 0000000..a940ffd
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties
@@ -0,0 +1,4 @@
+
+-bundles: version
+
+-exports: version
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java b/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java
new file mode 100644
index 0000000..0c69bc5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package version;
+
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+public class Version {
+    static {
+        versions = new HashMap();
+        register("version");
+    }
+  
+    private static Map versions;
+  
+    public static void register(String module) {
+        try {
+            InputStream moduleVersion = Version.class.getResourceAsStream("/"+module+".properties");
+            Properties props = new Properties();
+            props.load(moduleVersion);
+            String version = (String)props.get("version");
+            versions.put(module, version);
+            System.out.println("--- using "+module+" v"+version);
+        } catch (Exception ex) {
+            System.err.println("an error occured while registering "+module+": "+ex.getMessage());
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/sigil/bld-ivy/sigil-ivy-plugin.bnd b/sigil/bld-ivy/sigil-ivy-plugin.bnd
new file mode 100644
index 0000000..7fb4156
--- /dev/null
+++ b/sigil/bld-ivy/sigil-ivy-plugin.bnd
@@ -0,0 +1,23 @@
+# sigil-ivy-plugin.jar
+
+Bundle-DocURL: http://sigil.codecauldron.org
+Bundle-Vendor: Paremus Limited
+Bundle-Version: ${version}
+
+# we depend on bndlib, but don't want to embed it,
+# in case other tasks want a different version.
+# So just add Class-Path to the manifest, so we load bndlib.jar
+# if it's in the same directory as the sigil-ivy-plugin.
+Class-Path: bndlib.jar
+
+# embed other dependencies directly in the plugin
+Private-Package: \
+  org.cauldron.*, \
+  profiles.*, \
+  org.eclipse.core.runtime, \
+  org.osgi.framework
+
+-removeheaders: TODAY, DSTAMP, TSTAMP
+-pedantic: true
+
+# end
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java
new file mode 100644
index 0000000..572b64f
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class BundleInfoTask extends Task {
+	private File bundle;
+	private String header;
+	private String property;
+	private String defaultValue;
+	
+	@Override
+	public void execute() throws BuildException {
+		if (bundle == null)
+			throw new BuildException("missing attribute: bundle");
+		if (header == null)
+			throw new BuildException("missing attribute: header");
+		
+		try {
+			JarFile jar = new JarFile(bundle);
+			Manifest mf = jar.getManifest();
+			String value = mf.getMainAttributes().getValue(header);
+			if ( property == null ) {
+				log(header + "=" + value);
+			}
+			else {
+			    if ("Bundle-SymbolicName".equals(header) && value != null) {
+			        // remove singleton flag
+			        int semi = value.indexOf(';');
+			        if (semi > 0)
+			            value = value.substring(0, semi);
+			    }
+				if ( value == null ) {
+					value = defaultValue;
+				}
+				if ( value != null ) {
+					getProject().setNewProperty(property, value);
+				}
+			}
+		} catch (IOException e) {
+			throw new BuildException( "Failed to access bundle", e);
+		}
+	}
+	
+	public void setBundle(String bundle) {
+		this.bundle = new File( bundle );
+	}
+	
+	public void setHeader(String header) {
+		this.header = header;
+	}
+	
+	public void setProperty(String property) {
+		this.property = property;
+	}
+	
+	public void setDefaultValue(String defaultValue) {
+		this.defaultValue = defaultValue;
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java
new file mode 100644
index 0000000..f8ab762
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+
+public class BundleTask extends Task {
+	private File[] classpath;
+	private String destPattern;
+	private boolean force;
+	private String property;
+	private String sigilFile;
+
+	@Override
+	public void execute() throws BuildException {
+		if (classpath == null)
+			throw new BuildException("missing: attribute: classpathref");
+		if (destPattern == null)
+			throw new BuildException("missing attribute: destpattern");
+		
+		IBldProject project;
+
+		try {
+			project = BldFactory.getProject(getSigilFileURI());
+
+		} catch (IOException e) {
+			throw new BuildException("failed to get project file: " + e);
+		}
+		
+		Properties env = new Properties();
+		@SuppressWarnings("unchecked") Hashtable<String, String> properties = getProject().getProperties();
+		for (String key : properties.keySet()) {
+			if (key.matches("^[a-z].*")) {    // avoid props starting with Uppercase - bnd adds them to manifest
+				env.setProperty(key, properties.get(key));
+			}
+		}
+		
+		BundleBuilder bb = new BundleBuilder(project, classpath, destPattern, env);
+		boolean anyModified = false;
+
+		for (IBldBundle bundle : project.getBundles()) {
+			String id = bundle.getId();
+			log("creating bundle: " + id);
+			int nWarn = 0;
+			int nErr = 0;
+			String msg = "";
+			
+			try {
+				boolean modified = (bb.createBundle(bundle, force, new BundleBuilder.Log() {
+					public void warn(String msg) {
+						log(msg, Project.MSG_WARN);
+					}
+					public void verbose(String msg) {
+						log(msg, Project.MSG_VERBOSE);
+					}
+				}));
+				nWarn = bb.warnings().size();
+				if (modified) {
+					anyModified = true;
+				} else {
+					msg = " (not modified)";
+				}
+			} catch (Exception e) {
+				List<String> errors = bb.errors();
+				if (errors != null) {
+    				nErr = errors.size();
+    				for (String err : errors) {
+    					log(err, Project.MSG_ERR);
+    				}
+				}
+				throw new BuildException("failed to create: " + id + ": " + e, e);
+			} finally {
+				log(id + ": " + count(nErr, "error") + ", " + count(nWarn, "warning") + msg);
+			}
+		}
+		
+		if (anyModified && property != null) {
+			getProject().setProperty(property, "true");
+		}
+	}
+	
+	private URI getSigilFileURI() {
+		File file = sigilFile == null ? new File(getProject().getBaseDir(), IBldProject.PROJECT_FILE) : new File(sigilFile);
+		if ( !file.isFile() ) {
+			throw new BuildException( "File not found " + file.getAbsolutePath() );
+		}
+		return file.toURI();
+	}
+
+	private String count(int count, String msg) {
+		return count + " " + msg + (count == 1 ? "" : "s");
+	}
+
+	public void setDestpattern(String pattern) {
+		this.destPattern = pattern;
+	}
+	
+	public void setForce(String force) {
+		this.force = Boolean.parseBoolean(force);
+	}
+	
+	public void setProperty(String property) {
+		this.property = property;
+	}
+
+	public void setClasspathref(String value) {
+		Path p = (Path) getProject().getReference(value);
+		if (p == null) {
+			throw new BuildException(value + "is not a path reference.");
+		}
+
+		String[] paths = p.list();
+		classpath = new File[paths.length];
+		for (int i = 0; i < paths.length; ++i) {
+			classpath[i] = new File(paths[i]);
+		}
+	}
+
+	public void setSigilFile(String sigilFile) {
+		this.sigilFile = sigilFile;
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java
new file mode 100644
index 0000000..ce5d9db
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.config.IRepositoryConfig;
+import org.cauldron.sigil.repository.AbstractRepositoryManager;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+
+public class BldRepositoryManager extends AbstractRepositoryManager {
+	private static Map<String, String> aliases = new HashMap<String, String>();
+
+	static {
+		aliases.put("filesystem", "org.cauldron.bld.core.repository.FileSystemRepositoryProvider");
+		aliases.put("obr", "org.cauldron.bld.obr.OBRRepositoryProvider");
+		aliases.put("project", "org.cauldron.bld.ivy.ProjectRepositoryProvider");
+		aliases.put("system", "org.cauldron.bld.core.repository.SystemRepositoryProvider");
+	};
+
+	private Map<String, Properties> repos;
+
+	public BldRepositoryManager(Map<String, Properties> repos) {
+		this.repos = repos;
+	}
+
+	@Override
+	protected void loadRepositories() {
+		for (String name : repos.keySet()) {
+			Properties repo = repos.get(name);
+
+			String alias = repo.getProperty(IRepositoryConfig.REPOSITORY_PROVIDER);
+			if (alias == null) {
+				Log.error("provider not specified for repository: " + name);
+				continue;
+			}
+			
+			String provider = (aliases.containsKey(alias) ? aliases.get(alias) : alias);
+
+            if (alias.equals("obr")) {
+				// cache is directory where synchronized bundles are stored;
+				// not needed in ivy.
+				repo.setProperty("cache", "/no-cache");
+				
+				if (!repo.containsKey("index")) {
+    				// index is created as copy of url
+    				File indexFile = new File(System.getProperty("java.io.tmpdir"), "obr-index-" + name);
+    				indexFile.deleteOnExit();
+    				repo.setProperty("index", indexFile.getAbsolutePath());
+				}
+			}
+
+			int level = Integer.parseInt(repo.getProperty(IRepositoryConfig.REPOSITORY_LEVEL,
+					IBundleRepository.NORMAL_PRIORITY + ""));
+
+			try {
+				IRepositoryProvider instance = (IRepositoryProvider) (Class.forName(provider).newInstance());
+				IBundleRepository repository = instance.createRepository(name, repo);
+				addRepository(repository, level);
+				Log.verbose("added repository: " + repository);
+			} catch (Exception e) {
+				throw new Error("createRepository() failed: " + repo + " : " + e, e);
+			}
+		}
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java
new file mode 100644
index 0000000..f29f656
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+
+public class BldResolver implements IBldResolver {
+	private Map<String, Properties> repos;
+	private BldRepositoryManager manager;
+	
+	static {
+		try {
+			BldCore.init();
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	};
+	
+	public BldResolver(Map<String,Properties> repos) {
+		this.repos = repos;
+	}
+
+	public IResolution resolve(IModelElement element, boolean transitive) {
+		int options = ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INCLUDE_OPTIONAL;
+		if (transitive) options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+		
+		ResolutionConfig config = new ResolutionConfig(options);
+		try {
+			return resolve(element, config);
+		} catch (ResolutionException e) {
+			throw new IllegalStateException("eek! this shouldn't happen when ignoreErrors=true", e);
+		}
+	}
+		
+	public IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException {
+		int options = 0;
+		if ( transitive ) options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+		ResolutionConfig config = new ResolutionConfig(options);
+		return resolve(element, config);
+	}
+	
+	private IResolution resolve(IModelElement element, ResolutionConfig config) throws ResolutionException {
+		if (manager == null) {
+			manager = new BldRepositoryManager(repos);
+		}
+
+		IResolutionMonitor nullMonitor = new IResolutionMonitor() {
+			public void endResolution(IModelElement requirement, ISigilBundle sigilBundle) {
+			}
+
+			public boolean isCanceled() {
+				return false;
+			}
+
+			public void startResolution(IModelElement requirement) {
+			}
+		};
+
+		return manager.getBundleResolver().resolve(element, config, nullMonitor);
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java
new file mode 100644
index 0000000..e914172
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class FindUtil {
+	static final String WILD_ANY = "[^.].*";
+	static final String WILD_ONE = "[^.][^;]*";    // WILD_ONE.endsWith(WILD_ANY) == false
+	
+	// example pattern: ${repository}/projects/abc*/*/project.sigil
+	public static Collection<File> findFiles(String pattern) throws IOException {
+		int star = pattern.indexOf("*");
+		if (star == -1) {
+			throw new IOException("pattern doesn't contain '*': " + pattern);
+		}
+		int slash = pattern.lastIndexOf('/', star);
+		
+		String regex = pattern.substring(slash + 1);
+		regex = regex.replaceAll("\\*\\*", "-wildany-");
+		regex = regex.replaceAll("\\*", "-wildone-");
+		regex = regex.replaceAll("-wildany-", WILD_ANY);
+		regex = regex.replaceAll("-wildone-", WILD_ONE);
+		
+		String[] patterns = regex.split("/");
+		
+		ArrayList<File> list = new ArrayList<File>();
+		File root = new File(pattern.substring(0, slash));
+		
+		if (root.isDirectory()) {
+    		findFiles(root, 0, patterns, list);
+		} else {
+			throw new IOException("pattern root directory does not exist: " + root);
+		}
+		
+		return list;
+	}
+	
+	private static void findFiles(File dir, int level, String[] patterns, Collection<File> list) {
+		final String filePattern = patterns[patterns.length-1];
+		final String dirPattern;
+		
+		if (level < patterns.length-1) {
+			dirPattern = patterns[level];
+		} else {
+			dirPattern = "/";    // impossible to match marker
+		}
+		
+		final boolean stillWild;
+		if ((level > 0) && (level < patterns.length) && patterns[level-1].endsWith(WILD_ANY)) {
+			stillWild = true;
+		} else {
+			stillWild = false;
+		}
+		
+		for (File path : dir.listFiles(new FileFilter() {
+			public boolean accept(File pathname) {
+				String name = pathname.getName();
+				if (pathname.isDirectory()) {
+					return name.matches(dirPattern) || (stillWild && name.matches(WILD_ANY));
+				} else if (dirPattern.equals("/") || dirPattern.equals(WILD_ANY)) {
+					return name.matches(filePattern);
+				} else {
+					return false;
+				}
+			}
+		})) {
+			if (path.isDirectory()) {
+				int inc = path.getName().matches(dirPattern) ? 1 : 0;
+				findFiles(path, level + inc, patterns, list);
+			} else {
+				list.add(path);
+			}
+		}
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java
new file mode 100644
index 0000000..35ff995
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionException;
+
+public interface IBldResolver {
+	IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException;
+	IResolution resolve(IModelElement element, boolean transitive);
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java
new file mode 100644
index 0000000..6724063
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import org.apache.ivy.util.Message;
+
+// ensure common prefix to all sigil messages
+public class Log {
+	public static final String PREFIX = "Sigil: ";
+	
+	private static final boolean highlight = false;
+	
+	public static void error(String msg) {
+		if (highlight)
+    		Message.deprecated(PREFIX + "[error] " + msg);
+		Message.error(PREFIX + msg);
+	}
+
+	public static void warn(String msg) {
+		if (highlight)
+    		Message.deprecated(PREFIX + "[warn] " + msg);
+		else
+    		Message.warn(PREFIX + msg);
+	}
+	
+	public static void info(String msg) {
+		if (highlight)
+    		Message.deprecated(PREFIX + "[info] " + msg);
+		else
+    		Message.info(PREFIX + msg);
+	}
+
+	public static void verbose(String msg) {
+		Message.verbose(PREFIX + "[verbose] " + msg);
+	}
+	
+	public static void debug(String msg) {
+		if (highlight)
+    		Message.deprecated(PREFIX + "[debug] " + msg);
+		else
+    		Message.debug(PREFIX + msg);
+	}
+
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java
new file mode 100644
index 0000000..8dbca34
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+import org.osgi.framework.Version;
+
+public class ProjectRepository extends AbstractBundleRepository {
+    private ArrayList<ISigilBundle> bundles;
+    private ArrayList<ISigilBundle> wildBundles;
+    private String projectFilePattern;
+
+    /* package */ProjectRepository(String id, String projectFilePattern) {
+        super(id);
+        this.projectFilePattern = projectFilePattern.replaceAll("\\[sigilproject\\]",
+                IBldProject.PROJECT_FILE);
+    }
+
+    @Override
+    public void accept(IRepositoryVisitor visitor, int options) {
+        for (ISigilBundle b : getBundles()) {
+            if (!visitor.visit(b)) {
+                break;
+            }
+        }
+    }
+
+    // override to provide fuzzy matching for wild-card exports.
+    @Override
+    public Collection<ISigilBundle> findAllProviders(final IPackageImport pi, int options) {
+        return findProviders(pi, options, false);
+    }
+
+    @Override
+    public ISigilBundle findProvider(IPackageImport pi, int options) {
+        Collection<ISigilBundle> found = findProviders(pi, options, true);
+        return found.isEmpty() ? null : found.iterator().next();
+    }
+
+    private Collection<ISigilBundle> findProviders(final IPackageImport pi, int options,
+            boolean findFirst) {
+        ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+        ILicensePolicy policy = findPolicy(pi);
+        String name = pi.getPackageName();
+        VersionRange versions = pi.getVersions();
+
+        // find exact match(es)
+        for (ISigilBundle bundle : getBundles()) {
+            if (policy.accept(bundle)) {
+                for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+                    if (name.equals(exp.getPackageName())
+                            && versions.contains(exp.getVersion())) {
+                        found.add(bundle);
+                        if (findFirst)
+                            return found;
+                    }
+                }
+            }
+        }
+
+        if (!found.isEmpty())
+            return found;
+
+        // find best fuzzy match
+        ISigilBundle fuzzyMatch = null;
+        int fuzzyLen = 0;
+
+        for (ISigilBundle bundle : getWildBundles()) {
+            if (policy.accept(bundle)) {
+                for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+                    String export = exp.getPackageName();
+                    if (export.endsWith("*")) {
+                        String export1 = export.substring(0, export.length() - 1);
+                        if ((name.startsWith(export1) || export1.equals(name + "."))
+                                && versions.contains(exp.getVersion())) {
+                            if (export1.length() > fuzzyLen) {
+                                fuzzyLen = export1.length();
+                                fuzzyMatch = bundle;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (fuzzyMatch != null)
+            found.add(fuzzyMatch);
+
+        return found;
+    }
+
+    private synchronized void init() {
+        System.out.println("Sigil: loading Project Repository: " + projectFilePattern);
+
+        ArrayList<File> projects = new ArrayList<File>();
+
+        for (String pattern : projectFilePattern.split("\\s+")) {
+            try {
+                Collection<File> files = FindUtil.findFiles(pattern);
+                if (files.isEmpty()) {
+                    Log.warn("ProjectRepository: no projects match: " + pattern);
+                } else {
+                    projects.addAll(files);
+                }
+            } catch (IOException e) {
+                // pattern root dir does not exist
+                Log.error("ProjectRepository: " + pattern + ": " + e.getMessage());
+            }
+        }
+
+        if (projects.isEmpty()) {
+            throw new IllegalArgumentException(
+                    "ProjectRepository: no projects found using pattern: "
+                            + projectFilePattern);
+        }
+
+        bundles = new ArrayList<ISigilBundle>();
+
+        for (File proj : projects) {
+            try {
+                addBundles(proj, bundles);
+            } catch (IOException e) {
+                Log.warn("Skipping project: " + proj + ": " + e.getMessage());
+            } catch (ParseException e) {
+                Log.warn("Skipping project: " + proj + ": " + e.getMessage());
+            }
+        }
+    }
+
+    private List<ISigilBundle> getBundles() {
+        if (bundles == null) {
+            init();
+        }
+        return bundles;
+    }
+
+    private List<ISigilBundle> getWildBundles() {
+        if (wildBundles == null) {
+            wildBundles = new ArrayList<ISigilBundle>();
+            for (ISigilBundle bundle : getBundles()) {
+                for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+                    String export = exp.getPackageName();
+                    if (export.endsWith("*")) {
+                        wildBundles.add(bundle);
+                        break;
+                    }
+                }
+            }
+        }
+        return wildBundles;
+    }
+
+    public void refresh() {
+        bundles = null;
+        wildBundles = null;
+        notifyChange();
+    }
+
+    private void addBundles(File file, List<ISigilBundle> list) throws IOException,
+            ParseException {
+        URI uri = file.getCanonicalFile().toURI();
+        IBldProject project = BldFactory.getProject(uri);
+
+        for (IBldBundle bb : project.getBundles()) {
+            IBundleModelElement info = new BundleModelElement();
+
+            for (IPackageExport pexport : bb.getExports()) {
+                info.addExport(pexport);
+            }
+
+            for (IPackageImport import1 : bb.getImports()) {
+                IPackageImport clone = (IPackageImport) import1.clone();
+                clone.setParent(null);
+                info.addImport(clone);
+            }
+
+            for (IRequiredBundle require : bb.getRequires()) {
+                IRequiredBundle clone = (IRequiredBundle) require.clone();
+                clone.setParent(null);
+                info.addRequiredBundle(clone);
+            }
+
+            info.setSymbolicName(bb.getSymbolicName());
+
+            Version version = new Version(bb.getVersion());
+            info.setVersion(version);
+
+            ProjectBundle pb = new ProjectBundle();
+            pb.setBundleInfo(info);
+            pb.setId(bb.getId());
+
+            ModuleDescriptor md = SigilParser.instance().parseDescriptor(uri.toURL());
+
+            ModuleRevisionId mrid = md.getModuleRevisionId();
+            pb.setModule(mrid.getName());
+            pb.setOrg(mrid.getOrganisation());
+            // XXX: should revision be configurable?
+            pb.setRevision("latest." + md.getStatus());
+
+            list.add(pb);
+            Log.debug("ProjectRepository: added " + pb);
+            Log.debug("ProjectRepository: exports " + bb.getExports());
+        }
+    }
+
+    public static class ProjectBundle extends SigilBundle {
+        private String id;
+        private String module;
+        private String org;
+        private String revision;
+
+        @Override
+        public String toString() {
+            return "ProjectBundle[" + org + "@" + module + (id == null ? "" : "$" + id)
+                    + "#" + revision + "]";
+        }
+
+        public String getModule() {
+            return module;
+        }
+
+        public void setModule(String module) {
+            this.module = module;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getRevision() {
+            return revision;
+        }
+
+        public void setRevision(String rev) {
+            this.revision = rev;
+        }
+
+        public String getOrg() {
+            return org;
+        }
+
+        public void setOrg(String org) {
+            this.org = org;
+        }
+    }
+
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java
new file mode 100644
index 0000000..e04dc60
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+
+public class ProjectRepositoryProvider implements IRepositoryProvider{
+	private static HashMap<String, ProjectRepository> cache = new HashMap<String, ProjectRepository>();
+	
+	public IBundleRepository createRepository(String id, Properties properties) throws RepositoryException {
+		ProjectRepository repository = cache.get(id);
+		
+		if (repository == null) {
+    		String pattern = properties.getProperty("pattern");
+    		if (pattern == null) {
+    		    throw new RepositoryException("property 'pattern' not specified.");
+    		}
+            repository = new ProjectRepository(id, pattern);
+            cache.put(id, repository);
+		}
+		
+        return repository;
+    }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java
new file mode 100644
index 0000000..1ab3772
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java
@@ -0,0 +1,587 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IResolution;
+
+public class SigilParser implements ModuleDescriptorParser {
+
+	private static DelegateParser instance;
+
+	// used by ProjectRepository
+    static DelegateParser instance() {
+		if (instance == null)
+			throw new IllegalStateException("SigilParser is not instantiated.");
+		return instance;
+	}
+
+	public SigilParser() {
+		if (instance == null) {
+			instance = new DelegateParser();
+		}
+	}
+	
+	/**
+	 * In IvyDE, IvyContext is not available, so we can't find the SigilResolver.
+	 * This allows us to construct one.
+	 * @deprecated temporary to support IvyDE
+	 */
+	public void setConfig(String config) {
+		instance.config = config;
+	}
+
+	/**
+	 * sets delegate parser.
+	 * If not set, we delegate to the default Ivy parser.
+	 * @param type name returned by desired parser's getType() method.
+	 */
+	public void setDelegateType(String type) {
+		for (ModuleDescriptorParser parser : ModuleDescriptorParserRegistry.getInstance().getParsers()) {
+			if (parser.getType().equals(type)) {
+				instance.delegate = parser;
+				break;
+			}
+		}
+
+		if (instance.delegate == null) {
+			throw new IllegalArgumentException("Can't find parser delegateType=" + type);
+		}
+	}
+
+	/**
+	 * sets default file name the delegate parser accepts.
+	 * If not set, defaults to "ivy.xml".
+	 * @param name
+	 */
+	public void setDelegateFile(String name) {
+		instance.ivyFile = name;
+	}
+
+	/**
+	 * sets the dependencies to keep from the delegate parser.
+	 * If not set, all existing dependencies are dropped.
+	 * @param regex pattern matching dependency names to keep.
+	 */
+	public void setKeepDependencies(String regex) {
+		instance.keepDependencyPattern = Pattern.compile(regex);
+	}
+	
+	/**
+	 * reduce level of info logging.
+	 * @param quiet
+	 */
+	public void setQuiet(String quiet) {
+		instance.quiet = Boolean.parseBoolean(quiet);
+	}
+
+	/**
+	 * sets the SigilResolver we use.
+	 * If not set, we use the first SigilResolver we find.
+	 * @param name
+	 */
+	public void setResolver(String name) {
+		instance.resolverName = name;
+	}
+
+	/**
+	 * adds override element: <override name="X" pattern="Y" replace="Z"/>. Overrides
+	 * Ivy variables using a pattern substitution on the resource directory path.
+	 * 
+	 * @deprecated
+	 * This is only needed when a delegate parser expects Ant variables to be set
+	 * during the ivy:buildlist task (or the ProjectRepository initialisation),
+	 * which they are not. The delegate parser should really be fixed, as otherwise
+	 * the ivy:buildlist task won't work without this workaround.
+	 */
+	// e.g. <override name="ant.project.name" pattern=".*/([^/]+)/([^/]+)$" replace="$1-$2"/>
+	public void addConfiguredOverride(Map<String, String> var) {
+		final String name = var.get("name");
+		final String regex = var.get("pattern");
+		final String replace = var.get("replace");
+
+		if (name == null || regex == null || replace == null)
+			throw new IllegalArgumentException("override must contain name, pattern and replace attributes.");
+
+		instance.varRegex.put(name, Pattern.compile(regex));
+		instance.varReplace.put(name, replace);
+	}
+
+	// implement ModuleDescriptorParser interface by delegation to singleton instance.
+
+	public boolean accept(Resource res) {
+		return instance.accept(res);
+	}
+
+	public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
+		return instance.getMetadataArtifact(mrid, res);
+	}
+
+	public String getType() {
+		return instance.getType();
+	}
+
+	public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
+			throws ParseException, IOException {
+		return instance.parseDescriptor(settings, xmlURL, validate);
+	}
+
+	public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
+			throws ParseException, IOException {
+		return instance.parseDescriptor(settings, xmlURL, res, validate);
+	}
+
+	public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md) throws ParseException,
+			IOException {
+		instance.toIvyFile(source, res, destFile, md);
+	}
+
+	/**
+	 * delegating parser.
+	 * (optionally) removes original dependencies and augments with sigil-determined dependencies.
+	 */
+	static class DelegateParser extends XmlModuleDescriptorParser {
+
+		private static final String SIGIL_BUILDLIST = IBldProject.PROJECT_FILE;
+		private static final String KEEP_ALL = ".*";
+		
+		private IBldResolver resolver;
+		private ParserSettings defaultSettings;
+		private Map<String, DefaultModuleDescriptor> rawCache = new HashMap<String, DefaultModuleDescriptor>();
+		private Map<String, DefaultModuleDescriptor> augmentedCache = new HashMap<String, DefaultModuleDescriptor>();
+
+		private HashMap<String, String> varReplace = new HashMap<String, String>();
+		private HashMap<String, Pattern> varRegex = new HashMap<String, Pattern>();
+
+		private ModuleDescriptorParser delegate;
+		private String ivyFile = "ivy.xml";
+		private String resolverName;
+		private Pattern keepDependencyPattern;
+    	private boolean quiet;
+    	private String config;
+
+		@Override
+		public boolean accept(Resource res) {
+			boolean accept = (res instanceof SigilResolver.SigilIvy)
+					|| res.getName().endsWith("/" + SIGIL_BUILDLIST)
+					|| ((instance.getSigilURI(res) != null) &&
+						((instance.delegate != null ? instance.delegate.accept(res) : false) || super.accept(res)));
+			if (accept)
+    			Log.verbose("accepted: " + res);
+			return accept;
+		}
+
+		@Override
+		public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md)
+				throws ParseException, IOException {
+			Log.verbose("writing resource: " + res + " toIvyFile: " + destFile);
+			// source allows us to keep layout and comments,
+			// but this doesn't work if source is not ivy.xml.
+			// So just write file directly from descriptor.
+			try {
+				XmlModuleDescriptorWriter.write(md, destFile);
+			} finally {
+				if (source != null)
+					source.close();
+			}
+		}
+		
+		@Override
+		public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
+				throws ParseException, IOException {
+			return parseDescriptor(settings, xmlURL, new URLResource(xmlURL), validate);
+		}
+
+		@Override
+		public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
+				throws ParseException, IOException {
+			String cacheKey = xmlURL.toString() + settings.hashCode();
+			DefaultModuleDescriptor dmd = augmentedCache.get(cacheKey);
+
+			if (dmd == null) {
+				dmd = rawCache.get(cacheKey);
+				if (dmd == null) {
+					dmd = delegateParse(settings, xmlURL, res, validate);
+    			}
+
+				if (!quiet)
+    				Log.info("augmenting module=" + dmd.getModuleRevisionId().getName() +
+						" ant.project.name=" + settings.substitute("${ant.project.name}"));
+				
+				addDependenciesToDescriptor(res, dmd);
+				augmentedCache.put(cacheKey, dmd);
+				
+				Log.verbose("augmented dependencies: " + Arrays.asList(dmd.getDependencies()));
+			}
+
+			return dmd;
+		}
+		
+		// used by ProjectRepository
+		ModuleDescriptor parseDescriptor(URL projectURL) throws ParseException, IOException {
+			if (defaultSettings == null) {
+				Ivy ivy = IvyContext.getContext().peekIvy();
+				if (ivy == null)
+					throw new IllegalStateException("can't get default settings - no ivy context.");
+				defaultSettings = ivy.getSettings();
+			}
+			
+			URL ivyURL = new URL(projectURL, ivyFile);
+			String cacheKey = ivyURL.toString() + defaultSettings.hashCode();
+			DefaultModuleDescriptor dmd = rawCache.get(cacheKey);
+			
+			if (dmd == null) {
+				URLResource res = new URLResource(ivyURL);
+				// Note: this doesn't contain the augmented dependencies, which is OK,
+				// since the ProjectRepository only needs the id and status.
+				dmd = delegateParse(defaultSettings, ivyURL, res, false);
+				rawCache.put(cacheKey, dmd);
+			}
+
+			return dmd;
+		}
+
+		private DefaultModuleDescriptor delegateParse(ParserSettings settings, URL xmlURL, Resource res,
+				boolean validate) throws ParseException, IOException {
+			String resName = res.getName();
+
+			if (resName.endsWith("/" + SIGIL_BUILDLIST)) {
+				String name = resName.substring(0, resName.length() - SIGIL_BUILDLIST.length());
+				res = res.clone(name + ivyFile);
+				xmlURL = new URL(xmlURL, ivyFile);
+			}
+
+			if (settings instanceof IvySettings) {
+				IvySettings ivySettings = (IvySettings) settings;
+				String dir = new File(res.getName()).getParent();
+
+				for (String name : varRegex.keySet()) {
+					Pattern regex = varRegex.get(name);
+					String replace = varReplace.get(name);
+
+					String value = regex.matcher(dir).replaceAll(replace);
+
+					Log.debug("overriding variable " + name + "=" + value);
+					ivySettings.setVariable(name, value);
+				}
+			}
+
+			ModuleDescriptor md = null;
+			if (delegate == null || !delegate.accept(res)) {
+				md = super.parseDescriptor(settings, xmlURL, res, validate);
+			} else {
+				md = delegate.parseDescriptor(settings, xmlURL, res, validate);
+			}
+
+			return mungeDescriptor(md, res);
+		}
+
+		/**
+		 * clones descriptor and removes dependencies, as descriptor MUST have
+		 * 'this' as the parser given to its constructor.
+		 * Only dependency names matched by keepDependencyPattern are kept,
+		 * as we're going to add our own dependencies later.
+		 */
+		private DefaultModuleDescriptor mungeDescriptor(ModuleDescriptor md, Resource res) {
+
+			if ((md instanceof DefaultModuleDescriptor) &&
+					(md.getParser() == this) &&
+					(KEEP_ALL.equals(keepDependencyPattern))) {
+				return (DefaultModuleDescriptor) md;
+			}
+				
+			DefaultModuleDescriptor dmd = new DefaultModuleDescriptor(this, res);
+
+			dmd.setModuleRevisionId(md.getModuleRevisionId());
+			dmd.setPublicationDate(md.getPublicationDate());
+
+			for (Configuration c : md.getConfigurations()) {
+				String conf = c.getName();
+
+				dmd.addConfiguration(c);
+
+				for (Artifact a : md.getArtifacts(conf)) {
+					dmd.addArtifact(conf, a);
+				}
+			}
+
+			if (keepDependencyPattern != null) {
+    			for (DependencyDescriptor dependency : md.getDependencies()) {
+    				String name = dependency.getDependencyId().getName();
+    				if (keepDependencyPattern.matcher(name).matches()) {
+    					dmd.addDependency(dependency);
+    				}
+    			}
+			}
+
+			return dmd;
+		}
+
+		/*
+		 * find URI to Sigil project file. This assumes that it is in the same
+		 * directory as the ivy file.
+		 */
+		private URI getSigilURI(Resource res) {
+			URI uri = null;
+
+			if (res instanceof URLResource) {
+				URL url = ((URLResource) res).getURL();
+				uri = URI.create(url.toString()).resolve(IBldProject.PROJECT_FILE);
+				try {
+					InputStream stream = uri.toURL().openStream(); // check file
+																	// exists
+					stream.close();
+				} catch (IOException e) {
+					uri = null;
+				}
+			} else if (res instanceof FileResource) {
+				uri = ((FileResource) res).getFile().toURI().resolve(IBldProject.PROJECT_FILE);
+				if (!(new File(uri).exists()))
+					uri = null;
+			}
+
+			return uri;
+		}
+
+		/*
+		 * add sigil dependencies to ModuleDescriptor.
+		 */
+		private void addDependenciesToDescriptor(Resource res, DefaultModuleDescriptor md) throws IOException {
+			// FIXME: transitive should be configurable
+			final boolean transitive = true; // ivy default is true
+			final boolean changing = false;
+			final boolean force = false;
+
+			URI uri = getSigilURI(res);
+			if (uri == null)
+				return;
+
+			IBldProject project;
+
+			project = BldFactory.getProject(uri);
+
+			IBundleModelElement requirements = project.getDependencies();
+			Log.verbose("requirements: " + Arrays.asList(requirements.children()));
+
+			// preserve version range for Require-Bundle
+			// XXX: synthesise bundle version range corresponding to package version ranges?
+			HashMap<String, VersionRange> versions = new HashMap<String, VersionRange>();
+			for (IModelElement child : requirements.children()) {
+				if (child instanceof IRequiredBundle) {
+					IRequiredBundle bundle = (IRequiredBundle) child;
+					versions.put(bundle.getSymbolicName(), bundle.getVersions());
+				}
+			}
+
+			IBldResolver resolver = findResolver();
+			if (resolver == null) {
+				// this can happen in IvyDE, but it retries and is OK next time.
+				Log.warn("failed to find resolver - IvyContext not yet available.");
+				return;
+			}
+			
+			IResolution resolution = resolver.resolve(requirements, false);
+			Log.verbose("resolution: " + resolution.getBundles());
+
+			ModuleRevisionId masterMrid = md.getModuleRevisionId();
+			DefaultDependencyDescriptor dd;
+			ModuleRevisionId mrid;
+
+			for (ISigilBundle bundle : resolution.getBundles()) {
+				IBundleModelElement info = bundle.getBundleInfo();
+				String name = info.getSymbolicName();
+
+				if (name == null) {
+					// e.g. SystemProvider with framework=null
+					continue;
+				}
+
+				if (bundle instanceof ProjectRepository.ProjectBundle) {
+					ProjectRepository.ProjectBundle pb = (ProjectRepository.ProjectBundle) bundle;
+					String org = pb.getOrg();
+					if (org == null)
+						org = masterMrid.getOrganisation();
+					String id = pb.getId();
+					String module = pb.getModule();
+					String rev = pb.getRevision();
+
+					mrid = ModuleRevisionId.newInstance(org, module, rev);
+					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+
+					if (!id.equals(module)) { // non-default artifact
+						dd.addDependencyArtifact(module,
+								new DefaultDependencyArtifactDescriptor(dd, id, "jar", "jar", null, null));
+					}
+				} else {
+					VersionRange version = versions.get(name);
+					String rev = version != null ? version.toString() : info.getVersion().toString();
+					mrid = ModuleRevisionId.newInstance(SigilResolver.ORG_SIGIL, name, rev);
+					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+				}
+
+				int nDeps = 0;
+				boolean foundDefault = false;
+
+				// TODO: make dependency configurations configurable SIGIL-176
+				for (String conf : md.getConfigurationsNames()) {
+					if (conf.equals("default")) {
+						foundDefault = true;
+					} else if (md.getArtifacts(conf).length == 0) {
+						dd.addDependencyConfiguration(conf, conf + "(default)");
+						nDeps++;
+					}
+				}
+
+				if (nDeps > 0) {
+					if (foundDefault)
+						dd.addDependencyConfiguration("default", "default");
+				} else {
+					dd.addDependencyConfiguration("*", "*");    // all configurations
+				}
+
+				md.addDependency(dd);
+			}
+
+			boolean resolved = true;
+			for (IModelElement child : requirements.children()) {
+				ISigilBundle provider = resolution.getProvider(child);
+				if (provider == null) {
+					resolved = false;
+					// this is parse phase, so only log verbose message.
+					// error is produced during resolution phase.
+					Log.verbose("WARN: can't resolve: " + child);
+
+					String name;
+					String version;
+					if (child instanceof IRequiredBundle) {
+						IRequiredBundle rb = (IRequiredBundle) child;
+						name = rb.getSymbolicName();
+						version = rb.getVersions().toString();
+					} else {
+						IPackageImport pi = (IPackageImport) child;
+						name = "import!" + pi.getPackageName();
+						version = pi.getVersions().toString();
+					}
+
+					mrid = ModuleRevisionId.newInstance("!" + SigilResolver.ORG_SIGIL, name, version);
+					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+					dd.addDependencyConfiguration("*", "*"); // all
+					// configurations
+					md.addDependency(dd);
+				}
+			}
+
+			if (!resolved) {
+				// Ivy does not show warnings or errors logged from parse phase, until after resolution.
+				// but <buildlist> ant task doesn't do resolution, so errors would be silently ignored.
+				// Hence, we must use Message.info to ensure this failure is seen.
+				if (!quiet)
+    				Log.info("WARN: resolution failed in: " + masterMrid.getName() + ". Use -v for details.");
+			}
+		}
+
+		/*
+		 * find named resolver, or first resolver that implements IBldResolver.
+		 */
+		private IBldResolver findResolver() {
+			if (resolver == null) {
+				Ivy ivy = IvyContext.getContext().peekIvy();
+				if (ivy != null) {
+    				if (resolverName != null) {
+    					DependencyResolver r = ivy.getSettings().getResolver(resolverName);
+    					if (r == null) {
+    						throw new Error("SigilParser: resolver \"" + resolverName + "\" not defined.");
+    					} else if (r instanceof IBldResolver) {
+    						resolver = (IBldResolver) r;
+    					} else {
+    						throw new Error("SigilParser: resolver \"" + resolverName + "\" is not a SigilResolver.");
+    					}
+    				} else {
+    					for (Object r : ivy.getSettings().getResolvers()) {
+    						if (r instanceof IBldResolver) {
+    							resolver = (IBldResolver) r;
+    							break;
+    						}
+    					}
+    				}
+    
+    				if (resolver == null) {
+    					throw new Error("SigilParser: can't find SigilResolver. Check ivysettings.xml.");
+    				}
+				} else if (config != null) {
+					Log.warn("creating duplicate resolver to support IvyDE.");
+					resolver = new SigilResolver();
+					((SigilResolver)resolver).setConfig(config);
+				}
+			}
+			return resolver;
+		}
+	}
+
+}
+
+/*
+ * this is only needed so that we can distinguish sigil-added dependencies from
+ * existing ones.
+ */
+class SigilDependencyDescriptor extends DefaultDependencyDescriptor {
+	public SigilDependencyDescriptor(DefaultModuleDescriptor md, ModuleRevisionId mrid, boolean force,
+			boolean changing, boolean transitive) {
+		super(md, mrid, force, changing, transitive);
+	}
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java
new file mode 100644
index 0000000..596aaba
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import static java.lang.String.format;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.FileUtil;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionException;
+
+/**
+ * This resolver is able to work with Sigil repositories.
+ * It does not allow publishing.
+ */
+public class SigilResolver extends BasicResolver implements IBldResolver {
+	/** the sigil-injected dependency organisation name */
+	static final String ORG_SIGIL = "sigil";
+	
+	private IBldResolver resolver;
+	private String config;
+	private boolean extractBCP = false;
+	private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
+	
+	/**
+	 * no-args constructor required by Ivy.
+	 * 
+	 * XXX: It doesn't currently seem to matter that Ivy instantiates this many times,
+	 * since SigilParser caches it, and the ProjectRepositoryProvider static cache
+	 * prevents it being re-loaded unnecessarily.
+	 * 
+	 * If this should become a problem, then we will need to delegate to a singleton instance,
+	 * as we have done in SigilParser.
+	 */
+	public SigilResolver() {
+	}
+	
+	public void setConfig(String config) {
+		this.config = config;
+	}
+	
+	/**
+	 * if true, resolver extracts any jars embedded in Bundle-ClassPath.
+	 */
+	public void setExtractBCP(String extract) {
+		this.extractBCP = Boolean.parseBoolean(extract);
+	}
+
+	private IBldResolver getResolver() {
+		if (resolver == null) {
+			if (config == null) {
+				throw new Error("SigilResolver: not configured. Specify config=\"PATH\" in ivysettings.xml.");
+			}
+			try {
+				URI uri;
+				File file = new File(config);
+				
+				if (file.isAbsolute()) {
+					uri = file.toURI();
+				} else {
+    				URI cwd = new File(".").toURI();
+    				uri = cwd.resolve(config);
+				}
+				
+				Map<String, Properties> repositories = BldFactory.getConfig(uri).getRepositoryConfig();
+				resolver = new BldResolver(repositories);
+			} catch (IOException e) {
+				throw new Error("SigilResolver: failed to configure: " + e);
+			}
+		}
+		return resolver;
+	}
+
+	public IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException {
+		return getResolver().resolveOrFail(element, transitive);
+	}
+
+	public IResolution resolve(IModelElement element, boolean transitive) {
+		return getResolver().resolve(element, transitive);
+	}
+
+	public String getTypeName() {
+		return "sigil";
+	}
+
+	/*
+	 * synthesize an ivy descriptor for a Sigil dependency. called after Sigil
+	 * resolution, so descriptor already contains resolved version.
+	 */
+	public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+		ResolvedResource ref = null;
+
+		ModuleRevisionId id = dd.getDependencyRevisionId();
+
+		if (!id.getOrganisation().equals(ORG_SIGIL)) {
+			return null;
+		}
+
+		ISigilBundle bundle = resolve(id);
+		if (bundle == null) {
+			return null;
+		}
+
+		String symbolicName = id.getName();
+		String version = bundle.getVersion().toString();
+
+		Resource res = new SigilIvy(extractBCP ? bundle : null, symbolicName, version);
+		ref = new ResolvedResource(res, version);
+
+		Log.debug(format("findIvyFileRef: dd=%s => ref=%s", dd, ref));
+		return ref;
+	}
+
+	@Override
+	protected ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+		String name = artifact.getName();
+		ModuleRevisionId id = artifact.getModuleRevisionId();
+
+		if (!id.getOrganisation().equals(ORG_SIGIL)) {
+			return null;
+		}
+		
+		ISigilBundle bundle = resolve(id);
+		if (bundle == null) {
+			return null;
+		}
+
+		IBundleModelElement info = bundle.getBundleInfo();
+		URI uri = info.getUpdateLocation();
+		Resource res = null;
+
+		try {
+			URL url =  (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+    		if (name.contains("!")) {
+    			String[] split = name.split("!");
+    			url = new URL("jar:" + url + "!/" + split[1] + "." + artifact.getExt());
+    		}
+			res = new URLResource(url);
+		} catch (MalformedURLException e) {
+			System.out.println("Oops! " + e);
+		}
+
+		String version = bundle.getVersion().toString();
+		ResolvedResource ref = new ResolvedResource(res, version);
+
+		Log.debug(format("findArtifactRef: artifact=%s, date=%s => ref=%s", artifact, date, ref));
+		return ref;
+	}
+
+	private ISigilBundle resolve(ModuleRevisionId id) {
+		String revision = id.getRevision();
+		String range = revision;
+
+		if (revision.indexOf(',') < 0) {
+			// SigilParser has already resolved the revision from the import
+			// version range.
+			// We now need to locate the same bundle to get its download URL.
+			// We must use an OSGi version range to specify the exact version,
+			// otherwise it will resolve to "specified version or above".
+			range = "[" + revision + "," + revision + "]";
+		}
+
+		RequiredBundle bundle = new RequiredBundle();
+		bundle.setSymbolicName(id.getName());
+		bundle.setVersions(VersionRange.parseVersionRange(range));
+
+		try {
+			IResolution resolution = resolveOrFail(bundle, false);
+			ISigilBundle[] bundles = resolution.getBundles().toArray(new ISigilBundle[0]);
+			if (bundles.length == 1) {
+				return bundles[0];
+			}
+		} catch (ResolutionException e) {
+			return null;
+		}
+		return null;
+	}
+
+	/*
+	 * Implement BasicResolver abstract methods
+	 */
+
+	@Override
+	protected long get(Resource res, File dest) throws IOException {
+		FileUtil.copy(res.openStream(), dest, null);
+		long len = res.getContentLength();
+		Log.debug(format("get(%s, %s) = %d", res, dest, len));
+		return len;
+	}
+
+
+	@Override
+	public Resource getResource(String source) throws IOException {
+		source = encode(source);
+		Resource res = resourcesCache.get(source);
+		if (res == null) {
+			res = new URLResource(new URL(source));
+			resourcesCache.put(source, res);
+		}
+		Log.debug(format("SIGIL: getResource(%s) = %d", source, res));
+		return res;
+	}
+
+	private static String encode(String source) {
+		return source.trim().replaceAll(" ", "%20");
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected Collection findNames(Map tokenValues, String token) {
+		throw new Error("SigilResolver: findNames not supported.");
+	}
+
+	public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+		throw new Error("SigilResolver: publish not supported.");
+	}
+
+	public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
+		// stop publish attempts being silently ignored.
+		throw new Error("SigilResolver: publish not supported.");
+	}
+
+	/*
+	 * Synthesize a virtual ivy file for a Sigil dependency.
+	 */
+	static class SigilIvy implements Resource {
+		private StringBuilder content = new StringBuilder();
+		private String name;
+		private boolean exists = true;
+
+		private SigilIvy() {
+		}
+
+		public SigilIvy(ISigilBundle bundle, String module, String rev) {
+			this.name = "sigil!" + module + "#" + rev;
+
+			String org = ORG_SIGIL;
+			// FIXME: make status and pub configurable
+			String status = "release";
+			String pub = "20080912162859";
+
+			content.append("<ivy-module version=\"1.0\">\n");
+
+			content.append(format(
+					"<info organisation=\"%s\" module=\"%s\" revision=\"%s\" status=\"%s\" publication=\"%s\"/>\n",
+					org, module, rev, status, pub));
+			
+			String bcp = readBundleClassPath(bundle);
+			if (bcp != null) {
+    			content.append("<publications>\n");
+    			for (String j : bcp.split(",\\s*")) {
+    				if (j.equals(".")) {
+            			content.append("<artifact/>\n");
+    				} else if (!j.endsWith("component-activator.jar")) {
+    					if (j.endsWith(".jar"))
+    						j = j.substring(0, j.length() - 4);
+            			content.append(format("<artifact name=\"%s!%s\"/>\n", module, j));
+        			}
+    			}
+    			content.append("</publications>\n");
+			}
+
+			// TODO: add dependencies?
+			// <dependencies>
+			// <dependency org="org.apache" name="log4j" rev="1.2.12"
+			// revConstraint="[1.2,1.3)"/>
+			// </dependencies>
+
+			content.append("</ivy-module>\n");
+		}
+		
+        private String readBundleClassPath(ISigilBundle bundle) {
+        	if (bundle == null)
+        		return null;
+        	
+        	URI uri = bundle.getBundleInfo().getUpdateLocation();
+            InputStream is = null;
+			try {
+    			URL url =  (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+				is = url.openStream();
+            	JarInputStream js = new JarInputStream(is, false);
+                Manifest m = js.getManifest();
+                if (m != null)
+                    return (String) m.getMainAttributes().getValue("Bundle-ClassPath");
+			} catch (IOException e) {
+			} finally {
+				if (is != null) {
+					try {
+						is.close();
+					} catch (IOException e2) {
+					}
+				}
+			}
+			
+            return null;
+        }
+
+		public String toString() {
+			return "SigilIvy[" + name + "]";
+		}
+
+		// clone is used to read checksum files
+		// so clone(getName() + ".sha1").exists() should be false
+		public Resource clone(String cloneName) {
+			Log.debug("SigilIvy: clone: " + cloneName);
+			SigilIvy clone = new SigilIvy();
+			clone.name = cloneName;
+			clone.exists = false;
+			return clone;
+		}
+
+		public boolean exists() {
+			return exists;
+		}
+
+		public long getContentLength() {
+			return content.length();
+		}
+
+		public long getLastModified() {
+			// TODO Auto-generated method stub
+			Log.debug("NOT IMPLEMENTED: getLastModified");
+			return 0;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public boolean isLocal() {
+			return false;
+		}
+
+		@SuppressWarnings("deprecation")
+		public InputStream openStream() throws IOException {
+			return new java.io.StringBufferInputStream(content.toString());
+		}
+	}
+}
diff --git a/sigil/bld-ivy/test/config/proj1/newton-project.xml b/sigil/bld-ivy/test/config/proj1/newton-project.xml
new file mode 100644
index 0000000..98a52cd
--- /dev/null
+++ b/sigil/bld-ivy/test/config/proj1/newton-project.xml
@@ -0,0 +1,35 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<sigil:bundle xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<classpathEntry>&lt;classpathentry kind="lib" path="lib/dependee.jar"/&gt;</classpathEntry>
+<classpathEntry>&lt;classpathentry kind="src" path="src"/&gt;</classpathEntry>
+<bundle>
+<bundle-version>0.0.0</bundle-version>
+<bundle-vendor>Paremus Limited</bundle-vendor>
+<bundle-symbolicName>org.example.helloworld</bundle-symbolicName>
+<fragment-host bundle="com.springsource.org.apache.commons.lang" version="[2.0.0,2.4.0)"/>
+<export-package package="org.example.helloworld.api" version="0.0.0"/><import-package package="org.cauldron.newton.command" version="1.2.0"/><import-package package="org.apache.log4j" version="[1.2.14,1.3)"/><import-package package="org.example.helloworld.api" version="0"/><import-package package="org.cauldron.newton.command.console" version="1.2.0"/></bundle>
+<sigil:composite xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<location>sca/org.example.helloworld.service.composite</location>
+</sigil:composite>
+<sigil:composite xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<location>sca/org.example.helloworld.cli.composite</location>
+</sigil:composite>
+</sigil:bundle>
diff --git a/sigil/bld-ivy/test/config/proj1/sigil.properties b/sigil/bld-ivy/test/config/proj1/sigil.properties
new file mode 100644
index 0000000..424cd8d
--- /dev/null
+++ b/sigil/bld-ivy/test/config/proj1/sigil.properties
@@ -0,0 +1,33 @@
+# proj1 sigil.properties
+
+version: 1.2.3.beta1
+
+-bundles: main
+
+-exports: standalone
+
+-imports: \
+  javax.swing, \
+  javax.servlet, \
+  org.apache.commons.logging, \
+  org.apache.commons.lang, \
+  org.apache.log4j, \
+
+ximports: \
+  !org.cauldron.newton.framework, \
+  !org.osgi.*, \
+  org.apache.log4j;version="1.2.3", \
+
+-fragment: \
+  com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)"
+
+-composites: \
+  ivy.xml
+
+-resources: \
+  version.properties
+
+header;Bundle-Vendor: Paremus Limited
+header;Random-Heading: this header is a bit like random
+
+# end
diff --git a/sigil/bld-ivy/test/config/sigil-defaults.properties b/sigil/bld-ivy/test/config/sigil-defaults.properties
new file mode 100644
index 0000000..784ef8e
--- /dev/null
+++ b/sigil/bld-ivy/test/config/sigil-defaults.properties
@@ -0,0 +1,6 @@
+# sigil defaults
+
+package;javax.servlet:			(2.4,3.0]
+package;org.apache.log4j:		[1.2.14,1.3)
+package;org.apache.commons.lang:	[2.0.0,2.4.0)
+
diff --git a/sigil/bld-ivy/test/dependence/build.xml b/sigil/bld-ivy/test/dependence/build.xml
new file mode 100644
index 0000000..acb37e9
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/build.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="dependence" default="clean">
+    
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean directories">
+        <delete includeemptydirs="true"> 
+            <fileset dir="settings" excludes="ivysettings.*, sigil*.properties" />
+		</delete>
+        <ant dir="dependee" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+    </target>
+
+    <!-- ================================= 
+          target: all              
+         ================================= -->
+    <target name="all" depends="clean" description="--> make the whole example of dependency">
+        <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+        <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+        <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+    </target>
+    
+</project>
diff --git a/sigil/bld-ivy/test/dependence/dependee/build.xml b/sigil/bld-ivy/test/dependence/dependee/build.xml
new file mode 100644
index 0000000..925d95a
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/build.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="dependee" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+    <!-- some variables used -->
+    <property name="lib.dir" value="${basedir}/lib" />
+    <property name="build.dir" value="${basedir}/build" />
+    <property name="classes.dir" value="${build.dir}/classes" />
+    <property name="src.dir" value="${basedir}/src" />
+
+    <!-- ivy properties used -->
+    <property name="ivy.settings.dir" value="../settings" />
+    <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+    <!-- paths used for compilation and run -->
+    <path id="lib.path.id">
+	<fileset dir="${lib.dir}" />
+    </path>
+    <path id="run.path.id">
+	<path refid="lib.path.id" />
+	<path location="${classes.dir}" />
+    </path>
+    
+    <property name="ivy.jar.file"
+	     value="/opt/apache-ivy-2.0.0/ivy-2.0.0.jar"/>
+
+    <!-- ================================= 
+          target: init
+         ================================= -->
+    <target name="init">
+      <taskdef resource="org/apache/ivy/ant/antlib.xml"
+		    uri="antlib:org.apache.ivy.ant"
+		    classpath="${ivy.jar.file}"/>
+      <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+	<taskdef name="sigil.bundle"
+	  classname="org.cauldron.bld.ant.BundleTask"
+	  classpath="${basedir}/../../../target/sigil-ivy-plugin.jar"/>
+    </target>
+
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="init"
+    	description="--> resolve and retrieve dependencies with ivy">
+        <ivy:retrieve />
+    </target>
+
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+	
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> description">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="standalone.Main"/>
+    </target>
+    
+    <!-- ================================= 
+          target: jar              
+         ================================= -->
+    <target name="jar" depends="compile" description="--> make a jar file for this project">
+        <propertyfile file="${classes.dir}/version.properties">
+	        <entry  key="version" type="int" operation="+" default="0" />
+		</propertyfile>
+        <property file="${classes.dir}/version.properties" />
+
+	<!--
+        <jar destfile="${build.dir}/${ant.project.name}.jar">
+            <fileset dir="${classes.dir}" />
+        </jar>
+	-->
+
+        <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+		classpathref="run.path.id" />
+
+    </target>
+
+    <!-- ================================= 
+          target: publish              
+         ================================= -->
+    <target name="publish" depends="jar" description="--> publish this project in the ivy repository">
+        <property name="revision" value="${version}"/>
+        <delete file="${build.dir}/ivy.xml"/>
+		<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+			resolver="projects"
+			pubrevision="${revision}" 
+			status="release"
+		/>
+        <echo message="project ${ant.project.name} released with version ${revision}" />
+    </target>
+
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean the project">
+        <delete includeemptydirs="true">
+            <fileset dir="${basedir}">
+            	<exclude name="src/**" />
+            	<exclude name="build.xml" />
+		    	<exclude name="ivy.xml" />
+		    	<exclude name="sigil.properties" />
+        	</fileset>
+    	</delete>
+    </target>
+</project>
diff --git a/sigil/bld-ivy/test/dependence/dependee/ivy.xml b/sigil/bld-ivy/test/dependence/dependee/ivy.xml
new file mode 100644
index 0000000..b250663
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/ivy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info organisation="org.apache" module="dependee"/>
+    <dependencies>
+	<!--
+        <dependency org="sigil" name="com.springsource.org.apache.log4j" rev="[1.2,1.3)"/>
+	-->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/dependence/dependee/sigil.properties b/sigil/bld-ivy/test/dependence/dependee/sigil.properties
new file mode 100644
index 0000000..9dd7d8a
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/sigil.properties
@@ -0,0 +1,25 @@
+# dependee sigil.properties
+# all initial comments are retained.
+
+version: 1.2.3.beta1
+
+-bundles: dependee
+
+-exports: standalone
+
+-imports: \
+  org.apache.commons.lang;version="[2.0.0,2.4.0)";resolve=compile;resolution=optional, \
+  org.apache.commons.logging, \
+  javax.servlet;version="(2.4,3.0]", \
+  org.apache.log4j;version="[1.2.14,1.3)", \
+  javax.swing, \
+
+-resources: \
+  version.properties
+
+header;Hello: World
+
+X-requires: \
+  com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java b/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java
new file mode 100644
index 0000000..12ffa27
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package standalone;
+
+import java.util.Properties;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * TODO write javadoc
+ */
+public class Main {
+    /**
+     * Returns the version of the project
+     * @return a string representation of the version, null if the version could not be retreived
+     */
+    public static String getVersion() {
+        Properties p = new Properties();
+        try {
+            p.load(Main.class.getResourceAsStream("/version.properties"));
+            String version = p.getProperty("version");
+            if (version != null) {
+                return String.valueOf(Integer.parseInt(version));
+            } 
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    
+    /**
+     * Return the same string with all words capitalized.
+     * @param str the string conatining the words to capitalize
+     * @return null if the string was null, the string with all words capitalized otherwise
+     */
+    public static String capitalizeWords(String str) {
+        System.out.println("    [" + Main.class.getName() + "] capitalizing string \"" + str + "\" using " + WordUtils.class.getName());
+        return WordUtils.capitalizeFully(str);
+    }
+    public static void main(String[] args) {
+        String message="sentence to capitalize";
+        System.out.println("standard message : " + message);
+        System.out.println("capitalized message : " + capitalizeWords(message));
+    }
+}
diff --git a/sigil/bld-ivy/test/dependence/depender/build.xml b/sigil/bld-ivy/test/dependence/depender/build.xml
new file mode 100644
index 0000000..01216b7
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/build.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="depender" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+    <!-- some variables used -->
+    <property name="lib.dir" value="${basedir}/lib" />
+    <property name="build.dir" value="${basedir}/build" />
+    <property name="classes.dir" value="${build.dir}/classes" />
+    <property name="src.dir" value="${basedir}/src" />
+
+    <!-- ivy properties used -->
+    <property name="ivy.settings.dir" value="../settings" />
+    <property file="${ivy.settings.dir}/ivysettings.properties" />
+    
+    <!-- paths used for compilation and run  -->
+    <path id="lib.path.id">
+        <fileset dir="${lib.dir}" />
+	</path>
+    <path id="run.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+
+    <property name="ivy.jar.file"
+	     value="/opt/apache-ivy-2.0.0/ivy-2.0.0.jar"/>
+
+    <!-- ================================= 
+          target: init
+         ================================= -->
+    <target name="init">
+      <taskdef resource="org/apache/ivy/ant/antlib.xml"
+		    uri="antlib:org.apache.ivy.ant"
+		    classpath="${ivy.jar.file}"/>
+      <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+    </target>
+
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="init"
+    	description="--> resolve and retrieve dependencies with ivy">
+        <ivy:retrieve />
+    </target>
+    
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}" dot="true"/>
+    </target>
+    
+    <!-- ================================= 
+          target: gen-graph
+         ================================= -->
+    <target name="gen-graph" depends="report" description="--> generates a graph of dependencies (requires dot in your path - see http://www.graphviz.org/)">
+    	<property name="dot.file" value="${build.dir}/apache-depending-default.dot" />
+    	<property name="ivygraph.output.file" value="${build.dir}/graph.png" />
+    	<exec executable="dot">
+    	    <arg line="-T png -o ${ivygraph.output.file} ${dot.file}" />
+    	</exec>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> description">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="clean, compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="depending.Main"/>
+    </target>
+    
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" description="--> clean the project">
+        <delete includeemptydirs="true">
+            <fileset dir="${basedir}">
+            	<exclude name="src/**" />
+            	<exclude name="build.xml" />
+		    	<exclude name="ivy.xml" />
+		    	<exclude name="sigil.properties" />
+        	</fileset>
+    	</delete>
+    </target>
+</project>
diff --git a/sigil/bld-ivy/test/dependence/depender/ivy.xml b/sigil/bld-ivy/test/dependence/depender/ivy.xml
new file mode 100644
index 0000000..f825b21
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/ivy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info organisation="org.apache" module="depender"/>
+    <dependencies>
+	<!--
+        <dependency name="dependee" rev="latest.integration" />
+	-->
+    </dependencies>
+    <conflicts>
+        <manager name="latest-compatible"/>
+    </conflicts>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/dependence/depender/sigil.properties b/sigil/bld-ivy/test/dependence/depender/sigil.properties
new file mode 100644
index 0000000..e76ec83
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/sigil.properties
@@ -0,0 +1,7 @@
+# depender sigil.properties
+
+-bundles: depender
+
+-imports: standalone
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java b/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java
new file mode 100644
index 0000000..db1b208
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package depending;
+
+/**
+ * TODO write javadoc
+ */
+public class Main {
+    public static void main(String[] args) {
+        String standaloneVersion = standalone.Main.getVersion();
+        if (standaloneVersion!=null) {
+            System.out.println("you are using version " + standaloneVersion + " of class " + standalone.Main.class.getName());
+        } else {
+            System.err.println("failed to get version of " + standalone.Main.class.getName());
+        }
+        String message = "i am " + Main.class.getName() + " and " + standalone.Main.class.getName() + " will do the job for me";
+        System.out.println("standard message : " + message);
+        System.out.println("capitalized message : " + standalone.Main.capitalizeWords(message));
+    }
+}
diff --git a/sigil/bld-ivy/test/dependence/settings/ivysettings.properties b/sigil/bld-ivy/test/dependence/settings/ivysettings.properties
new file mode 100644
index 0000000..f68f415
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/ivysettings.properties
@@ -0,0 +1,19 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+repository.dir=${ivy.settings.dir}/repository
diff --git a/sigil/bld-ivy/test/dependence/settings/ivysettings.xml b/sigil/bld-ivy/test/dependence/settings/ivysettings.xml
new file mode 100644
index 0000000..9312a85
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/ivysettings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+  <properties file="${ivy.settings.dir}/ivysettings.properties"/>
+  <settings defaultResolver="sigil-libs"/>
+
+  <!--<caches defaultCacheDir="${ivy.settings.dir}/ivy-cache" />
+  -->
+  <caches default="mycache" checkUpToDate="false">
+    <cache name="mycache" basedir="${ivy.settings.dir}/ivy-cache" useOrigin="true"/>
+  </caches>
+
+  <classpath file="${ivy.settings.dir}/../../../target/sigil-ivy-plugin.jar" />
+  <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+  <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+  <parsers>
+    <sigil-parser/>
+  </parsers>
+
+  <resolvers>
+    <sigil name="sigil-libs" config="${ivy.settings.dir}/sigil-repos.properties" />
+    <filesystem name="projects">
+      <ivy pattern="${repository.dir}/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/[artifact]-[revision].[ext]" />
+    </filesystem>
+</resolvers>
+<modules>
+  <module organisation="org.apache" name="dependee" resolver="projects"/>
+</modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties b/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties
new file mode 100644
index 0000000..f00cfaf
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties
@@ -0,0 +1,17 @@
+# repository config
+
+-repositories:	system, project, spring
+
+system;provider:	system
+system;level:		-1
+
+project;provider:	project
+project;level:		0
+project;pattern:	${..}/*/[sigilproject]
+
+spring;provider:	obr
+spring;level:		2
+spring;url:		http://sigil.codecauldron.org/spring-external.obr
+spring;index:		${..}/settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/sigil-defaults.properties b/sigil/bld-ivy/test/dependence/sigil-defaults.properties
new file mode 100644
index 0000000..2bc4bc9
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/sigil-defaults.properties
@@ -0,0 +1,2 @@
+header;Bundle-Vendor: Paremus Limited
+version: 1.2.3.parent
diff --git a/sigil/bld-ivy/test/multi-project/build.xml b/sigil/bld-ivy/test/multi-project/build.xml
new file mode 100644
index 0000000..fd2d774
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/build.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="all" default="order" xmlns:ivy="antlib:org.apache.ivy.ant">
+  <property name="common.dir" value="${basedir}/common" />
+  <import file="${common.dir}/common.xml"/>
+
+  <fileset id="projects" dir="projects" includes="**/build.xml"/>
+
+  <path id="unordered-list">
+    <fileset refid="projects"/>
+  </path>
+
+  <target name="buildlist" depends="load-ivy"> 
+    <ivy:buildlist reference="ordered-list">
+      <fileset refid="projects"/>
+    </ivy:buildlist>
+  </target>
+  
+  <target name="publish" depends="buildlist" 
+	description="compile, jar and publish all projects in the right order">
+    <subant target="publish" buildpathref="ordered-list">
+      <propertyset>
+	<propertyref name="ivy.loaded" />
+      </propertyset>
+    </subant>
+  </target>
+
+  <target name="clean" description="clean all projects">
+    <subant target="clean" buildpathref="unordered-list" />
+  </target>
+  
+  <target name="fullclean" depends="clean, load-ivy" 
+  description="clean tutorial: delete repository, ivy cache, and all projects">
+    <delete dir="repository"/>
+  	<ivy:cleancache />
+  </target>
+
+  <target name="order" depends="buildlist">
+    <subant target="hello" buildpathref="ordered-list" />
+  </target>
+
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/common/build.properties b/sigil/bld-ivy/test/multi-project/common/build.properties
new file mode 100644
index 0000000..9552b4a
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/build.properties
@@ -0,0 +1,30 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+lib.dir = ${basedir}/lib
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+src.dir = ${basedir}/src
+repository.dir=${common.dir}/../repository
+
+ivy.file = ${basedir}/ivy.xml
+
+jar.file = ${build.dir}/${ant.project.name}.jar
+main.class.name = ${ant.project.name}.Main
+
+module.version.target = 1.0
diff --git a/sigil/bld-ivy/test/multi-project/common/common.xml b/sigil/bld-ivy/test/multi-project/common/common.xml
new file mode 100644
index 0000000..4e54d1d
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/common.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="common" 
+         xmlns:ivy="antlib:org.apache.ivy.ant">
+	<!-- a sample common ant build file, used for ivy multi-project tutorial
+	     feel free to copy and adapt it to your own needs
+	     Note that the only targets specific to ivy are:
+	        load-ivy
+	     	resolve
+	     	report
+	     	ivy-new-version
+	     	publish
+	     	publish-local
+	     	
+	     All other targets are usual ant based targets, which could have been written
+	     in a build not depending at all on ivy: 
+	     resolve constructs a lib directory based upon ivy dependencies, and then the lib dir 
+	     is used as in any classical ant build
+	     -->
+	
+	<property file="${common.dir}/build.properties"/>
+	
+	<property name="ivy.jar.dir" value="/opt/apache-ivy-2.0.0-rc2" />
+	<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-2.0.0-rc2.jar" />
+        <property name="sigil-ivy-plugin.jar" value="${common.dir}/../../../target/sigil-ivy-plugin.jar"/>
+
+	<target name="hello">
+	    <echo message="${ant.project.name}"/>
+	</target>
+
+	<!-- ================================= 
+          target: load-ivy         
+            this target is not necessary if you put ivy.jar in your ant lib directory
+            if you already have ivy 2.0 in your ant lib, you can simply remove this
+            target
+         ================================= -->
+    <path id="ivy.lib.path">
+	<path location="${ivy.jar.file}"/>
+	<path location="${sigil-ivy-plugin.jar}"/>
+	<!--
+	<path location="${user.home}/src/tmp/ivy/build/ivy.jar"/>
+	-->
+    </path>
+
+    <target name="load-ivy" depends="ivy-taskdefs">
+	<ivy:settings file="${common.dir}/ivysettings.xml" />
+    </target>
+
+    <target name="ivy-taskdefs" unless="ivy.loaded">
+    	<property name="ivy.loaded" value="true"/>
+	<echo message="Loading Ivy ..."/>
+	<!--
+    	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+    	         uri="antlib:org.apache.ivy.ant" classpath="${ivy.jar.file}"/>
+	-->
+    	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+    	         uri="antlib:org.apache.ivy.ant"
+		 loaderRef="ivyLoader"
+		 classpath="${ivy.jar.file}"/>
+
+	<taskdef name="sigil.bundle"
+		 classname="org.cauldron.bld.ant.BundleTask"
+		 classpath="${sigil-ivy-plugin.jar}:${bndlib.jar}"/>
+    </target>    
+
+    <path id="lib.path.id">
+        <fileset dir="${lib.dir}" />
+    </path>
+
+    <path id="run.path.id">
+        <path refid="lib.path.id" />
+        <path location="${classes.dir}" />
+    </path>
+
+    
+	<!-- setup ivy default configuration with some custom info -->
+	<property name="ivy.local.default.root" value="${repository.dir}/local"/>
+	<property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+   	<!-- here is how we would have configured ivy if we had our own ivysettings file
+    <ivy:settings file="${common.dir}/ivysettings.xml" />
+    -->
+
+	
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="clean-lib, load-ivy" description="--> resolve and retrieve dependencies with ivy">
+        <mkdir dir="${lib.dir}"/> <!-- not usually necessary, ivy creates the directory IF there are dependencies -->
+    	
+    	<!-- the call to resolve is not mandatory, retrieve makes an implicit call if we don't -->
+    	<ivy:resolve file="${ivy.file}"/>
+    	<ivy:retrieve pattern="${lib.dir}/[artifact].[ext]" />
+    </target>
+    
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve" description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve" description="--> compile the project">
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" debug="true" />
+    </target>
+    
+    <!-- ================================= 
+          target: run
+         ================================= -->
+    <target name="run" depends="version, compile" description="--> compile and run the project">
+        <java classpathref="run.path.id" classname="${main.class.name}"/>
+    </target>
+
+	<target name="ivy-new-version" depends="load-ivy" unless="ivy.new.revision">
+    	<!-- default module version prefix value -->
+		<property name="module.version.prefix" value="${module.version.target}-dev-b" />
+		
+    	<!-- asks to ivy an available version number -->
+		<ivy:info file="${ivy.file}" />
+    	<ivy:buildnumber 
+    		organisation="${ivy.organisation}" module="${ivy.module}" 
+    		revision="${module.version.prefix}" defaultBuildNumber="1" revSep=""/>
+	</target>
+
+    <target name="local-version">
+		<tstamp>
+			<format property="now" pattern="yyyyMMddHHmmss"/>
+		</tstamp>
+        <property name="ivy.new.revision" value="${module.version.target}-local-${now}"/>
+    </target>
+	
+	<target name="version" depends="ivy-new-version">
+    	<!-- create version file in classpath for later inclusion in jar -->
+        <mkdir dir="${classes.dir}"/>
+		<echo message="version=${ivy.new.revision}" file="${classes.dir}/${ant.project.name}.properties" append="false" />
+
+		<!-- load generated version properties file -->
+        <property file="${classes.dir}/${ant.project.name}.properties" />
+    </target>
+
+    <!-- ================================= 
+          target: jar              
+         ================================= -->
+    <target name="jar" depends="version, compile" description="--> make a jar file for this project">
+
+	<!--
+        <jar destfile="${jar.file}">
+            <fileset dir="${classes.dir}" />
+            <manifest>
+                <attribute name="Built-By" value="${user.name}"/>
+                <attribute name="Build-Version" value="${version}" />
+            </manifest>
+        </jar>
+	-->
+
+        <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+                classpathref="run.path.id"/>
+    </target>
+
+    <!-- ================================= 
+          target: publish              
+         ================================= -->
+    <target name="publish" depends="clean-build, jar" description="--> publish this project in the ivy repository">
+    	<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+    			           resolver="shared"
+    			           pubrevision="${version}" 
+    			           status="release"
+    	/>
+        <echo message="project ${ant.project.name} released with version ${version}" />
+    </target>
+
+    <!-- ================================= 
+          target: publish-local              
+         ================================= -->
+    <target name="publish-local" depends="local-version, jar" description="--> publish this project in the local ivy repository">
+    	<ivy:publish artifactspattern="${build.dir}/[artifact].[ext]" 
+    			        resolver="local"
+    			        pubrevision="${version}"
+				        pubdate="${now}"
+    			        status="integration"
+    					forcedeliver="true"
+    	/>
+        <echo message="project ${ant.project.name} published locally with version ${version}" />
+    </target>
+
+	<!-- ================================= 
+          target: clean-local              
+         ================================= -->
+	<target name="clean-local" description="--> cleans the local repository for the current module">
+	   <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+	</target>
+
+	<!-- ================================= 
+          target: clean-lib              
+         ================================= -->
+    <target name="clean-lib" description="--> clean the project libraries directory (dependencies)">
+        <delete includeemptydirs="true" dir="${lib.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean-build              
+         ================================= -->
+    <target name="clean-build" description="--> clean the project built files">
+        <delete includeemptydirs="true" dir="${build.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" depends="clean-build, clean-lib" description="--> clean the project" />
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/common/ivysettings.xml b/sigil/bld-ivy/test/multi-project/common/ivysettings.xml
new file mode 100644
index 0000000..03440c4
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/ivysettings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+  <properties file="${ivy.settings.dir}/build.properties"/>
+  <caches defaultCacheDir="${ivy.settings.dir}/../ivy-cache" />
+
+  <settings defaultResolver="shared"
+     circularDependencyStrategy="error" />
+
+  <classpath file="${ivy.settings.dir}/../../../target/sigil-ivy-plugin.jar" />
+  <!--
+  Ant tasks "subant" and "ant" cause IvySettings to be re-loaded, 
+  which then re-loads SigilParser in a new URLClassLoader,
+  which causes ProjectRepository to be re-initialised. Outch!
+
+  Better if Ivy classpath cached its classloader, or provided a
+  loaderRef attribute, like ant does.
+
+  Loading sigil-ivy-plugin.jar in ivy-taskdefs, uses the same ClassLoader
+  to load sigil-ivy-plugin, and thus avoids re-initialising ProjectRepositiory.
+  -->
+  <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+  <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+  <parsers>
+    <sigil-parser quiet="true"/>
+  </parsers>
+
+  <resolvers>
+    <sigil name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+    <filesystem name="local">
+      <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+    </filesystem>
+    <filesystem name="shared">
+      <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+      <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+    </filesystem>
+  </resolvers>
+  <modules>
+    <!--<module organisation="org.apache.ivy.example" name=".*" resolver="shared"/>
+    -->
+    <module organisation="sigil" resolver="sigil"/>
+  </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh b/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh
new file mode 100755
index 0000000..2f7e184
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# sigilgen build.xml ..
+
+function a2s() {
+  find ${1:?} -name '*.java' -o -name '*.spring' -o -name '*.sca' |
+  perl -e '
+    use strict;
+    use FileHandle;
+
+    my %imports;
+    my %exports;
+
+    while (<>) {
+	chomp($_);
+	my $path = $_;
+	my $fh = new FileHandle($path);
+
+	my $testFile = ($path =~ m!Test[^/]+$!);
+
+	while (my $line = <$fh>) {
+	  chomp($line);
+
+	  if ($line =~ /^\s*package\s+([\w.]+)\s*;\s*$/) {
+	    my $exp = $1;
+	    if (!($testFile && ($exp !~ /^test[.]/))) {
+	      $exports{$exp} = 1;
+	    }
+	  } 
+	  elsif ($line =~ /^\s*import\s+([a-z_0-9.]+)([.][A-Z][^.]+)+;\s*$/) {
+	    my $imp = $1;
+	    $imports{$imp} = 1 unless ($imp =~ /^java[.]/);
+	  }
+	  elsif (($line !~ m!^\s*(//|\*|\@)!) &&
+		($line =~ /(\W)((([a-z0-9]{2,})[.]){3,})[A-Z]/)) {
+	    my $quot = $1;
+	    my $imp = $2;
+	    $imp =~ s/.$//;
+
+	    if (($imp !~ /^java[.]/) &&
+	    	($quot ne "\"" || ($path !~ /[.]java$/))) {
+	      $imports{$imp} = 1;
+	    }
+	  }
+	}
+	$fh->close();
+    }
+
+    if (keys(%exports) == () && keys(%imports) == ()) {
+      exit(1);
+    }
+
+    print "-exports: \\\n";
+    foreach my $key (sort keys(%exports)) {
+	print "\t$key, \\\n";
+    }
+    print "\n";
+
+    print "-imports: \\\n";
+    foreach my $key (sort keys(%imports)) {
+	print "\t$key, \\\n";
+    }
+    print "\n";
+
+    exit(0);
+  '
+  return $?
+}
+
+for file in $*; do
+  echo "converting $file"
+  name=$(perl -ne 'print $1 if (/project name="([^"]*)"/);' $file)
+  dir=$(dirname $file)
+
+  if [ -f $dir/sigil.properties ]; then
+    rm -f $dir/sigil.properties.old
+    mv $dir/sigil.properties $dir/sigil.properties.old
+  fi
+
+  (
+    echo "# generated by sigilgen on $(date)"
+    echo
+    echo "-bundles: $name"
+    echo
+    a2s $dir && mv $dir/sigil.properties-part $dir/sigil.properties
+  ) > $dir/sigil.properties-part
+
+  rm -f $dir/sigil.properties-part
+
+done
diff --git a/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties b/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties
new file mode 100644
index 0000000..d98b618
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties
@@ -0,0 +1,26 @@
+
+# repository config
+
+-repositories:	system, project, newton, spring
+
+system;provider:	system
+system;level:		-1
+#system;framework:	osgi.core.jar
+#system;profile:	J2SE-1.5
+
+project;provider:	project
+project;level:	0
+project;pattern:	../projects/**/[sigilproject]
+
+newton;provider:	filesystem
+newton;level:		1
+newton;recurse:		true
+#newton;dir:		/opt/newton-1.2.5/lib
+newton;dir:		/Users/derek/devl/Newton/src/target/newton-dist.dir/lib
+
+spring;provider:	obr
+spring;level:		2
+spring;url:		http://sigil.codecauldron.org/spring-external.obr
+spring;index:		../settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/build.properties b/sigil/bld-ivy/test/multi-project/projects/console/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/build.xml b/sigil/bld-ivy/test/multi-project/projects/console/build.xml
new file mode 100644
index 0000000..5c02cba
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="console" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml
new file mode 100644
index 0000000..93b5ee5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="console"
+        status="integration"/>
+    <dependencies>
+      <!--
+      <dependency name="version" rev="latest.integration" conf="default" />
+      <dependency name="list" rev="latest.integration" conf="default->standalone" />
+      <dependency name="find" rev="latest.integration" conf="default->standalone" />
+      <dependency name="sizewhere" rev="latest.integration" conf="default->standalone" />
+      -->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties
new file mode 100644
index 0000000..1d4bb13
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties
@@ -0,0 +1,6 @@
+
+-bundles: console
+
+-exports: console
+
+-requires: org.example.list
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java b/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java
new file mode 100644
index 0000000..678f3ca
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package console;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Arrays;
+import java.lang.reflect.Method;
+
+
+public class Main {
+    private static Collection QUIT_COMMANDS = Arrays.asList(new String[] {"quit", "q", "exit"});
+    private static Collection HELP_COMMANDS = Arrays.asList(new String[] {"help", "h", "?"});
+
+    public static void main(String[] a) throws Exception {
+      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+      while (true) {
+        System.out.print("> ");
+        String command = in.readLine().trim();
+        if (QUIT_COMMANDS.contains(command)) {
+          return;
+        }
+        if (HELP_COMMANDS.contains(command)) {
+          help();
+          continue;
+        }
+        String[] split = command.split(" ");
+        if (split.length == 0) {
+          error(command);
+          continue;
+        }
+        
+        try {
+          String[] args = new String[split.length - 1];
+          System.arraycopy(split, 1, args, 0, args.length);
+          Class cl = Class.forName(split[0]+".Main");
+          Method m = cl.getMethod("main", new Class[] {String[].class});
+          m.invoke(null, new Object[] {args});
+        } catch (Exception ex) {
+          error(command);
+          continue;
+        }
+      }
+    }
+    
+    private static void help() {
+      System.out.println("available commands:");
+      System.out.println("\tquit: quit the console");
+      System.out.println("\thelp: displays this message");
+      System.out.println("\tlist -dir <dir>: list files in given directory");
+      System.out.println("\tfind -dir <dir> -name <name>: list files with given name in given dir");
+      System.out.println("\tsizewhere -dir <dir> -name <name>: compute total size of files with given name in given dir");
+      System.out.println("\thelp: displays this message");
+    }
+            
+    private static void error(String command) {
+      System.out.println("unknown command "+command);
+      System.out.println("type ? for help");
+    }
+            
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/build.properties b/sigil/bld-ivy/test/multi-project/projects/find/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/build.xml b/sigil/bld-ivy/test/multi-project/projects/find/build.xml
new file mode 100644
index 0000000..4d23bf5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="find" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml
new file mode 100644
index 0000000..8026113
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="find"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="find" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <!--
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency name="list" rev="latest.integration" conf="core" />
+      <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="core->default" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+      -->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties
new file mode 100644
index 0000000..30c10a7
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: find
+
+-exports: find
+
+-imports:\
+  version, \
+  list, \
+  org.apache.commons.collections, \
+  org.apache.commons.cli
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java b/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java
new file mode 100644
index 0000000..25fa1af
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import version.Version;
+import list.ListFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+import  org.apache.commons.collections.CollectionUtils;
+import  org.apache.commons.collections.Predicate;
+
+public class FindFile {
+  static {
+    Version.register("find");
+  }
+  
+  public static Collection find(File dir, String name) {
+    return find(ListFile.list(dir), name);
+  }
+  
+  private static Collection find(Collection files, final String name) {    
+    return CollectionUtils.select(files, new Predicate() {
+      public boolean evaluate(Object o) {
+        return ((File)o).getName().indexOf(name) != -1;
+      }
+    });
+  }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java b/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java
new file mode 100644
index 0000000..a41c136
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName("dir")
+            .hasArg()
+            .withDescription("list files in given dir")
+            .create("dir");
+        Option name = OptionBuilder.withArgName("name")
+            .hasArg()
+            .withDescription("list files with given name")
+            .create("name");
+        Options options = new Options();
+
+        options.addOption(dir);
+        options.addOption(name);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+        Options options = getOptions();
+        try {
+        
+            CommandLineParser parser = new GnuParser();
+    
+            CommandLine line = parser.parse(options, args);
+            File dir = new File(line.getOptionValue("dir", "."));
+            String name = line.getOptionValue("name", "jar");
+            Collection files = FindFile.find(dir, name);
+            System.out.println("listing files in " + dir + " containing " + name);
+            for (Iterator it = files.iterator(); it.hasNext();) {
+                System.out.println("\t" + it.next() + "\n");
+            }
+        } catch(ParseException exp) {
+            // oops, something went wrong
+            System.err.println("Parsing failed.  Reason: " + exp.getMessage());
+              
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp("find", options);
+        }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/build.properties b/sigil/bld-ivy/test/multi-project/projects/list/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/build.xml b/sigil/bld-ivy/test/multi-project/projects/list/build.xml
new file mode 100644
index 0000000..3926148
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="list" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml
new file mode 100644
index 0000000..7fca587
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="list"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="list" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <!--
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+      -->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties
new file mode 100644
index 0000000..302cba7
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties
@@ -0,0 +1,14 @@
+
+# requirements
+
+-imports:\
+  version, \
+  org.apache.commons.cli
+
+# exports
+
+-bundles: list
+
+list;name: org.example.list
+list;-exports: list.*
+
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java b/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java
new file mode 100644
index 0000000..7467152
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import version.Version;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class ListFile {
+  static {
+    Version.register("list");
+  }
+  
+  public static Collection list(File dir) {
+    Collection files = new ArrayList();
+    
+    return list(dir, files);
+  }
+  
+  private static Collection list(File file, Collection files) {
+    if (file.isDirectory()) {
+      File[] f = file.listFiles();
+      for (int i=0; i<f.length; i++) {
+        list(f[i], files);
+      }
+    } else {
+      files.add(file);
+    }
+    return files;
+  }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java b/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java
new file mode 100644
index 0000000..0f8cb14
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName( "dir" )
+            .hasArg()
+            .withDescription(  "list files in given dir" )
+            .create( "dir" );
+        Options options = new Options();
+
+        options.addOption(dir);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+      Options options = getOptions();
+      try {
+        
+        CommandLineParser parser = new GnuParser();
+
+        CommandLine line = parser.parse( options, args );
+        File dir = new File(line.getOptionValue("dir", "."));
+        Collection files = ListFile.list(dir);
+        System.out.println("listing files in "+dir);
+        for (Iterator it = files.iterator(); it.hasNext(); ) {
+          System.out.println("\t"+it.next()+"\n");
+        }
+      } catch( ParseException exp ) {
+          // oops, something went wrong
+          System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
+          
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp( "list", options );
+      }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties b/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties
new file mode 100644
index 0000000..0000617
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties
@@ -0,0 +1,6 @@
+
+header;Bundle-Vendor: Paremus Limited
+
+package;org.osgi.framework: 1.7.1
+
+bad-key: wibble
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/build.properties b/sigil/bld-ivy/test/multi-project/projects/size/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/build.xml b/sigil/bld-ivy/test/multi-project/projects/size/build.xml
new file mode 100644
index 0000000..2a75fe5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="size" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml
new file mode 100644
index 0000000..33e9995
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="size"
+        status="integration"/>
+    <dependencies>
+      <!--
+      <dependency name="version" rev="latest.integration" conf="default" />
+      <dependency name="list" rev="latest.integration" conf="default->core" />
+      -->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties
new file mode 100644
index 0000000..cf239af
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties
@@ -0,0 +1,8 @@
+
+-bundles: size
+
+-exports: size
+
+-imports:\
+  list, \
+  version
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java b/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java
new file mode 100644
index 0000000..dcd4a1c
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package size;
+
+import version.Version;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+public class FileSize {
+  static {
+    Version.register("size");
+  }
+
+  public static long totalSize(File dir) {
+    return totalSize(list.ListFile.list(dir));
+  }
+  
+  public static long totalSize(Collection files) {
+    long total = 0;
+    for (Iterator it = files.iterator(); it.hasNext(); ) {
+      File f = (File)it.next();
+      total += f.length();
+    }
+    return total;
+  }  
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml
new file mode 100644
index 0000000..0b8ab4c
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="sizewhere" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml
new file mode 100644
index 0000000..6b86af0
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="sizewhere"
+        status="integration"/>
+    <configurations>
+      <conf name="core"/>
+      <conf name="standalone" extends="core"/>
+    </configurations>
+    <publications>
+      <artifact name="sizewhere" type="jar" conf="core" />
+    </publications>
+    <dependencies>
+      <!--
+      <dependency name="version" rev="latest.integration" conf="core->default" />
+      <dependency name="size" rev="latest.integration" conf="core->default" />
+      <dependency name="find" rev="latest.integration" conf="core" />
+      <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+      -->
+    </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties
new file mode 100644
index 0000000..c272f9b
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: sizewhere
+
+-exports: sizewhere
+
+-imports:\
+  find, \
+  size, \
+  version, \
+  org.apache.commons.cli
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java
new file mode 100644
index 0000000..131af66
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+    private static Options getOptions() {
+        Option dir = OptionBuilder.withArgName( "dir" )
+            .hasArg()
+            .withDescription(  "give total size of files in given dir" )
+            .create( "dir" );
+        Option name = OptionBuilder.withArgName( "name" )
+            .hasArg()
+            .withDescription(  "give total size of files with given name" )
+            .create( "name" );
+        Options options = new Options();
+
+        options.addOption(dir);
+        options.addOption(name);
+        
+        return options;
+    }
+    
+    public static void main(String[] args) throws Exception {
+      Options options = getOptions();
+      try {
+        
+        CommandLineParser parser = new GnuParser();
+
+        CommandLine line = parser.parse( options, args );
+        File dir = new File(line.getOptionValue("dir", "."));
+        String name = line.getOptionValue("name", "jar");
+        System.out.println("total size of files in "+dir+" containing "+name+": "+SizeWhere.totalSize(dir, name));
+      } catch( ParseException exp ) {
+          // oops, something went wrong
+          System.err.println( "Parsing failed.  Reason: " + exp.getMessage() );
+          
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp( "sizewhere", options );
+      }        
+    }
+            
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
new file mode 100644
index 0000000..3594bd9
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import version.Version;
+import size.FileSize;
+import find.FindFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class SizeWhere {
+  static {
+    Version.register("sizewhere");
+  }
+  
+  public static long totalSize(File dir, String name) {
+    return FileSize.totalSize(FindFile.find(dir, name));
+  }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/build.properties b/sigil/bld-ivy/test/multi-project/projects/version/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/build.properties
@@ -0,0 +1,21 @@
+#	 ***************************************************************
+#	 * Licensed to the Apache Software Foundation (ASF) under one
+#	 * or more contributor license agreements.  See the NOTICE file
+#	 * distributed with this work for additional information
+#	 * regarding copyright ownership.  The ASF licenses this file
+#	 * to you under the Apache License, Version 2.0 (the
+#	 * "License"); you may not use this file except in compliance
+#	 * with the License.  You may obtain a copy of the License at
+#	 * 
+#	 *   http://www.apache.org/licenses/LICENSE-2.0
+#	 * 
+#	 * Unless required by applicable law or agreed to in writing,
+#	 * software distributed under the License is distributed on an
+#	 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#	 * KIND, either express or implied.  See the License for the
+#	 * specific language governing permissions and limitations
+#	 * under the License.
+#	 ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/build.xml b/sigil/bld-ivy/test/multi-project/projects/version/build.xml
new file mode 100644
index 0000000..375db20
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="version" default="compile">
+	<property file="build.properties"/>
+	
+	<import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml
new file mode 100644
index 0000000..16e08f3
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.apache.ivy.example"
+        module="version"
+        status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties
new file mode 100644
index 0000000..a940ffd
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties
@@ -0,0 +1,4 @@
+
+-bundles: version
+
+-exports: version
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java b/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java
new file mode 100644
index 0000000..0c69bc5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package version;
+
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+public class Version {
+    static {
+        versions = new HashMap();
+        register("version");
+    }
+  
+    private static Map versions;
+  
+    public static void register(String module) {
+        try {
+            InputStream moduleVersion = Version.class.getResourceAsStream("/"+module+".properties");
+            Properties props = new Properties();
+            props.load(moduleVersion);
+            String version = (String)props.get("version");
+            versions.put(module, version);
+            System.out.println("--- using "+module+" v"+version);
+        } catch (Exception ex) {
+            System.err.println("an error occured while registering "+module+": "+ex.getMessage());
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/sigil/bld-junit/.classpath b/sigil/bld-junit/.classpath
new file mode 100644
index 0000000..d97a6bc
--- /dev/null
+++ b/sigil/bld-junit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<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.cauldron.sigil.core.classpathContainer"/>
+	<classpathentry kind="output" path="build/classes"/>
+</classpath>
diff --git a/sigil/bld-junit/.project b/sigil/bld-junit/.project
new file mode 100644
index 0000000..455b5c3
--- /dev/null
+++ b/sigil/bld-junit/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>bld-junit</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.cauldron.sigil.core.newtonBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.cauldron.sigil.core.newtonnature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs b/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..51d8662
--- /dev/null
+++ b/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Thu Feb 19 09:53:36 GMT 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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/bld-junit/build.xml b/sigil/bld-junit/build.xml
new file mode 100644
index 0000000..46ce9d1
--- /dev/null
+++ b/sigil/bld-junit/build.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<project name="bld-junit" default="bundle" basedir=".">
+  <import file="../bldcommon/common.xml"/>
+</project>
diff --git a/sigil/bld-junit/ivy.xml b/sigil/bld-junit/ivy.xml
new file mode 100644
index 0000000..9135f4a
--- /dev/null
+++ b/sigil/bld-junit/ivy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivy-module version="1.0">
+    <info 
+        organisation="org.cauldron"
+        module="bld.junit"
+        status="integration"/>
+    <publications>
+      <artifact name="org.cauldron.sigil.junit" />
+    </publications>
+</ivy-module>
diff --git a/sigil/bld-junit/sigil.properties b/sigil/bld-junit/sigil.properties
new file mode 100644
index 0000000..c22f88c
--- /dev/null
+++ b/sigil/bld-junit/sigil.properties
@@ -0,0 +1,22 @@
+
+# sigil project file, saved by plugin.
+
+-activator: org.cauldron.sigil.junit.activator.Activator
+
+version: 0.8.0
+
+-bundles: \
+	org.cauldron.sigil.junit, \
+
+-sourcedirs: \
+	src, \
+
+-exports: \
+	org.cauldron.sigil.junit.server, \
+
+-imports: \
+	junit.framework;version=4.5.0, \
+	org.osgi.framework;version=1.4.0, \
+	org.osgi.util.tracker;version=1.3.3, \
+
+# end
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java
new file mode 100644
index 0000000..0c84937
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit;
+
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import junit.framework.TestCase;
+
+public abstract class AbstractSigilTestCase extends TestCase {
+
+	private final static List<ServiceTracker> trackers = new LinkedList<ServiceTracker>();
+	
+	private BundleContext ctx;
+	
+	public void setBundleContext(BundleContext ctx) {
+		this.ctx = ctx;
+	}
+	
+	protected BundleContext getBundleContext() {
+		return ctx;
+	}
+	
+	@Override
+	protected void setUp() {
+		for ( Class<?> c : getReferences() ) {
+			ServiceTracker t = createBindTracker(c);
+			t.open();
+			trackers.add( t );
+		}
+	}
+
+	@Override
+	protected void tearDown() {
+		for ( ServiceTracker t : trackers ) {
+			t.close();
+		}
+		trackers.clear();
+	}
+
+
+	private ServiceTracker createBindTracker(final Class<?> c) {
+		return new ServiceTracker(ctx, c.getName(), new ServiceTrackerCustomizer() {
+			public Object addingService(ServiceReference reference) {
+				Object o = ctx.getService(reference);
+				Method m = getBindMethod(c);
+				if ( m != null ) invoke( m, o );
+				return o;
+			}
+
+			public void modifiedService(ServiceReference reference,
+					Object service) {
+			}
+
+			public void removedService(ServiceReference reference,
+					Object service) {
+				Method m = getUnbindMethod(c);
+				if ( m != null ) invoke( m, service );
+				ctx.ungetService(reference);
+			}
+		});
+	}
+	
+	private void invoke(Method m, Object o) {
+		try {
+			m.invoke( this,  new Object[] { o } );
+		} catch (Exception e) {
+			throw new IllegalStateException( "Failed to invoke binding method " + m, e);
+		}
+	}
+
+	protected abstract Class<?>[] getReferences();
+	
+	protected abstract Method getBindMethod(Class<?> clazz);
+	
+	protected abstract  Method getUnbindMethod(Class<?> clazz);
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java
new file mode 100644
index 0000000..2614113
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+public abstract class ReflectiveSigilTestCase extends AbstractSigilTestCase {
+
+	private Class<?>[] references;
+	private Map<Class<?>, Method> bindMethods;
+	private Map<Class<?>, Method> unbindMethods;
+	
+	@Override
+	protected Class<?>[] getReferences() {
+		introspect();
+		return references;
+	}
+
+	@Override
+	protected Method getBindMethod(Class<?> clazz) {
+		return bindMethods.get(clazz);
+	}
+
+	@Override
+	protected Method getUnbindMethod(Class<?> clazz) {
+		return unbindMethods.get(clazz);
+	}
+
+	private void introspect() {
+		if ( references == null ) {
+			bindMethods = findBindMethods(getClass(), "set", "add");
+			unbindMethods = findBindMethods(getClass(), "set", "remove");
+			
+			HashSet<Class<?>> refs = new HashSet<Class<?>>();
+			refs.addAll( bindMethods.keySet() );
+			refs.addAll( unbindMethods.keySet() );
+			references = refs.toArray( new Class<?>[refs.size()] );
+		}
+	}
+
+	private static Map<Class<?>, Method> findBindMethods(Class<?> clazz, String... prefix) {
+		HashMap<Class<?>, Method> found = new HashMap<Class<?>, Method>();
+		
+		checkDeclaredMethods(clazz, found, prefix);
+		
+		return found;
+	}
+	
+	private static void checkDeclaredMethods(Class<?> clazz, Map<Class<?>, Method> found, String...prefix) {
+		for ( Method m : clazz.getDeclaredMethods() ) {
+			if ( isMethodPrefixed(m, prefix) && isBindSignature(m) ) {
+				found.put( m.getParameterTypes()[0], m );
+			}
+		}
+		
+		Class<?> sup = clazz.getSuperclass();
+		if ( sup != null && sup != Object.class ) {
+			checkDeclaredMethods(sup, found, prefix);
+		}
+		
+		for ( Class<?> i : clazz.getInterfaces() ) {
+			checkDeclaredMethods(i, found, prefix);
+		}
+	}
+
+	private static boolean isMethodPrefixed(Method m, String...prefix) {
+		String n = m.getName();
+		for ( String p : prefix ) {
+			if ( n.startsWith( p ) && n.length() > p.length() ) {
+				return true;
+			}
+		}
+		return false;
+	}
+	private static boolean isBindSignature(Method m) {
+		return m.getReturnType() == Void.TYPE && m.getParameterTypes().length == 1;
+	}
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java
new file mode 100644
index 0000000..b22920b
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.activator;
+
+import org.cauldron.sigil.junit.server.JUnitService;
+import org.cauldron.sigil.junit.server.impl.JUnitServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author dave
+ */
+public class Activator implements BundleActivator {
+	private ServiceRegistration reg;
+	private JUnitServiceFactory service;
+
+	public void start(final BundleContext ctx) {
+		service = new JUnitServiceFactory();
+		service.start(ctx);
+		reg = ctx.registerService(JUnitService.class.getName(), service, null);
+    }
+
+    public void stop(BundleContext ctx) {
+    	reg.unregister();
+    	reg = null;
+    	service.stop(ctx);
+    	service = null;
+    }
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java
new file mode 100644
index 0000000..e111b26
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server;
+
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestSuite;
+
+public interface JUnitService {
+	Set<String> getTests();
+	
+	TestSuite createTest(String test);
+	
+	TestSuite createTest(String test, BundleContext ctx);
+}
\ No newline at end of file
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java
new file mode 100644
index 0000000..49fadf6
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.util.HashMap;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public class JUnitServiceFactory implements ServiceFactory {
+
+	private HashMap<String, Class<? extends TestCase>> tests = new HashMap<String, Class<? extends TestCase>>();
+	private TestClassListener listener;
+	
+	public void start(BundleContext ctx) {
+		listener = new TestClassListener(this);
+		ctx.addBundleListener(listener);
+		//listener.index(ctx.getBundle());
+		for ( Bundle b : ctx.getBundles() ) {
+			if ( b.getState() == Bundle.RESOLVED ) {
+				listener.index(b);
+			}
+		}
+	}
+	
+	public void stop(BundleContext ctx) {
+		ctx.removeBundleListener(listener);
+		listener = null;
+	}
+	
+	public Object getService(Bundle bundle, ServiceRegistration reg) {
+		return new JUnitServiceImpl(this, bundle.getBundleContext());
+	}
+
+	public void ungetService(Bundle bundle, ServiceRegistration reg, Object service) {
+	}
+
+	public void registerTest(Class<? extends TestCase> clazz) {
+		tests.put( clazz.getName(), clazz );
+	}
+
+	public void unregister(Class<? extends TestCase> clazz) {
+		tests.remove(clazz.getName());
+	}
+
+	public Set<String> getTests() {
+		return new TreeSet<String>(tests.keySet());
+	}
+
+	public TestSuite getTest(String test) {
+		Class<? extends TestCase> tc = tests.get(test);
+		return tc == null ? null : new TestSuite(tc);
+	}
+
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java
new file mode 100644
index 0000000..c549cea
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.cauldron.sigil.junit.server.JUnitService;
+import org.osgi.framework.BundleContext;
+
+public class JUnitServiceImpl implements JUnitService {
+	
+	private static final Logger log = Logger.getLogger(JUnitServiceImpl.class.getName());
+
+	private static final Class<?>[] BUNDLE_CONTEXT_PARAMS = new Class[] { BundleContext.class };
+
+	private final JUnitServiceFactory junitServiceFactory;
+	private final BundleContext bundleContext;
+
+	public JUnitServiceImpl(JUnitServiceFactory junitServiceFactory, BundleContext bundleContext) {
+		this.junitServiceFactory = junitServiceFactory;
+		this.bundleContext = bundleContext;
+	}
+
+	public Set<String> getTests() {
+		return junitServiceFactory.getTests();
+	}
+	
+	public TestSuite createTest(String test) {
+		return createTest(test, null);
+	}
+	
+	public TestSuite createTest(String test, BundleContext ctx) {
+		try {
+			TestSuite ts = junitServiceFactory.getTest(test);
+			
+			if ( ts == null ) return null;
+			
+			TestSuite ret = new TestSuite(ts.getName());
+			
+			Enumeration<Test> e = ts.tests();
+			
+			while ( e.hasMoreElements() ) {
+				Test t = e.nextElement();
+				setContext(t, ctx);
+				ret.addTest(t);
+			}
+			
+			return ret;
+		}
+		catch (final NoClassDefFoundError e) {
+			TestSuite s = new TestSuite(test);
+			s.addTest( new Test() {
+				public int countTestCases() {
+					return 1;
+				}
+
+				public void run(TestResult result) {
+					result.addError(this, e);
+				}
+			});
+			return s;
+		}
+		catch (final RuntimeException e) {
+			TestSuite s = new TestSuite(test);
+			s.addTest( new Test() {
+				public int countTestCases() {
+					return 1;
+				}
+
+				public void run(TestResult result) {
+					result.addError(this, e);
+				}
+				
+			});
+			return s;
+		}
+	}
+
+	private void setContext(Test t, BundleContext ctx) {
+		try {
+			Method m = findMethod( t.getClass(), "setBundleContext", BUNDLE_CONTEXT_PARAMS );
+			if ( m != null )
+				m.invoke(t, ctx == null ? bundleContext : ctx );
+		} catch (SecurityException e) {
+			log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+		} catch (IllegalArgumentException e) {
+			log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+		} catch (IllegalAccessException e) {
+			log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+		} catch (InvocationTargetException e) {
+			log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+		}
+	}
+
+	private Method findMethod(Class<?> clazz, String name,
+			Class<?>[] params) {
+		Method found = null;
+		
+		for ( Method m : clazz.getDeclaredMethods() ) {
+			if ( m.getName().equals(name) && Arrays.deepEquals(m.getParameterTypes(), params) ) {
+				found = m;
+				break;
+			}
+		}
+		
+		if ( found == null ) {
+			Class<?> c = clazz.getSuperclass();
+			
+			if ( c != null && c != Object.class ) {
+				found = findMethod(c, name, params);
+			}
+		}
+		
+		if ( found == null ) {
+			for ( Class<?> c : clazz.getInterfaces() ) {
+				found = findMethod(c, name, params);
+				if ( found != null ) {
+					break;
+				}
+			}
+		}
+		
+		return found;
+	}
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java
new file mode 100644
index 0000000..c3db3f9
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+public class TestClassListener implements SynchronousBundleListener {
+	private static final Logger log = Logger.getLogger(TestClassListener.class.getName());
+	
+	private final JUnitServiceFactory service;
+	
+	private HashMap<Long, Class<TestCase>[]> registrations = new HashMap<Long, Class<TestCase>[]>(); 
+	
+	public TestClassListener(JUnitServiceFactory service) {
+		this.service = service;
+	}
+	
+	public void bundleChanged(BundleEvent event) {
+		switch( event.getType() ) {
+		case BundleEvent.RESOLVED:
+			index( event.getBundle() );
+			break;
+		case BundleEvent.UNRESOLVED:
+			unindex( event.getBundle() );
+			break;
+		}
+	}
+
+	void index(Bundle bundle) {
+		if ( isTestBundle( bundle ) ) {
+			List<String> tests = findTests( bundle );
+			
+			if ( !tests.isEmpty() ) {
+				LinkedList<Class<? extends TestCase>> regs = new LinkedList<Class<? extends TestCase>>();
+				
+				for ( String jc : tests ) {
+					try {
+						Class<?> clazz = bundle.loadClass(jc);
+						if ( isTestCase(clazz) ) {
+							Class<? extends TestCase> tc = clazz.asSubclass(TestCase.class);
+							regs.add( tc );
+							service.registerTest(tc);
+						}
+					} catch (ClassNotFoundException e) {
+						log.log( Level.WARNING, "Failed to load class " + jc, e );
+					} catch (NoClassDefFoundError e) {
+						log.log( Level.WARNING, "Failed to load class " + jc, e );
+					}
+				}
+				
+				registrations.put( bundle.getBundleId(), toArray(regs) );
+			}
+		}
+	}
+
+	private boolean isTestBundle(Bundle bundle) {
+		try {
+			bundle.loadClass(TestCase.class.getName());
+			return true;
+		} catch (ClassNotFoundException e) {
+			return false;
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private Class<TestCase>[] toArray(LinkedList<Class<? extends TestCase>> regs) {
+		return regs.toArray( new Class[regs.size()] );
+	}
+
+	private boolean isTestCase(Class<?> clazz) {
+		return 
+			TestCase.class.isAssignableFrom(clazz) && 
+			!Modifier.isAbstract(clazz.getModifiers()) && 
+			!clazz.getPackage().getName().startsWith( "junit" );
+	}
+
+	void unindex(Bundle bundle) {
+		Class<TestCase>[] classes = registrations.remove(bundle.getBundleId());
+		if ( classes != null ) {
+			for ( Class<TestCase> tc : classes ) {
+				service.unregister(tc);
+			}
+		}
+	}
+	
+	private List<String> findTests(Bundle bundle) {
+		@SuppressWarnings("unchecked") Enumeration<URL> urls = bundle.findEntries("", "*.class", true);
+		
+		LinkedList<String> tests = new LinkedList<String>();
+		while( urls.hasMoreElements() ) { 
+			URL url = urls.nextElement();
+			tests.add( toClassName( url ) );
+		}
+		
+		return tests;
+	}
+
+	private String toClassName(URL url) {
+		String f = url.getFile();
+		String cn = f.substring(1, f.length() - 6 );
+		return cn.replace('/', '.');
+	}
+
+}
diff --git a/sigil/bldcommon/build.properties b/sigil/bldcommon/build.properties
new file mode 100644
index 0000000..ad4583b
--- /dev/null
+++ b/sigil/bldcommon/build.properties
@@ -0,0 +1,40 @@
+# common properties
+# easier to set here than in xml file
+
+# set common.dir when used without ant (e.g. IvyDE)
+common.dir	= ${ivy.settings.dir}
+
+ivy.jar		= ${common.dir}/../bld-ivy/lib/compile/ivy-2.0.0-rc1.jar
+sigil-ivy-plugin.jar = ${common.dir}/../bld-ivy/target/sigil-ivy-plugin.jar
+
+build.dir	= ${basedir}/build
+build_xml	= build.xml
+classes.dir	= ${build.dir}/classes
+deps.dir	= ${build.dir}/deps
+composite.dir	= ${basedir}/xml
+src.dir		= ${basedir}/src
+ivy.file	= ${basedir}/ivy.xml
+
+_build.dir	= ${build.dir}
+build.lib.dir	= ${_build.dir}/lib
+build.etc.dir	= ${_build.dir}/etc
+
+top-build.dir	= ${common.dir}/../build
+dist.dir	= ${top-build.dir}/dist
+install.dir	= ${top-build.dir}/install
+cache.dir	= ${top-build.dir}/ivy-cache
+repository.dir	= ${top-build.dir}/repository
+# note: clean-local task assumes repository.pattern starts with ${ivy.module}
+repository.pattern = [module]/[revision]/[type]s/[artifact].[ext]
+
+community.version = 1.4.0.SNAPSHOT
+jini.version	= 2.1
+paremus.version	= ${default.version}
+bundle.version	= ${paremus.version}
+resolve.log	= download-only
+
+# over-ridden by hudson
+buildVersion	= 0.8.0-dev
+
+# end
+
diff --git a/sigil/bldcommon/common.xml b/sigil/bldcommon/common.xml
new file mode 100644
index 0000000..957854c
--- /dev/null
+++ b/sigil/bldcommon/common.xml
@@ -0,0 +1,466 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="common" 
+         xmlns:ivy="antlib:org.apache.ivy.ant">
+    
+    <dirname property="common.dir" file="${ant.file.common}"/>
+    <property file="${common.dir}/sigil-defaults.properties" prefix="default."/>
+    <property file="${common.dir}/build.properties"/>
+
+    <!-- =====================================================
+         stuff intended to be over-ridden is prefixed with my.
+         =====================================================-->
+
+    <fileset id="my.projects" dir="${basedir}">
+      <include name="**/${build_xml}"/>
+    </fileset>
+
+    <path id="my.classpath">
+        <fileset dir="${basedir}" includes="lib/*.jar"/>
+    </path>
+
+    <path id="javac.classpath">
+	<path refid="my.classpath"/>
+        <fileset dir="${deps.dir}" />
+    </path>
+
+    <path id="sigil.classpath">
+        <path refid="javac.classpath" />
+        <path location="${classes.dir}" />
+    </path>
+
+    <!-- ================================= 
+          target: load-ivy
+         ================================= -->
+    <target name="load-ivy" depends="ident,ivy-taskdefs">
+	<ivy:settings file="${common.dir}/ivysettings.xml" />
+    </target>
+
+    <!-- ================================= 
+          target: ivy-taskdefs
+         ================================= -->
+    <target name="ivy-taskdefs" unless="ivy.loaded">
+    	<property name="ivy.loaded" value="true"/>
+		<echo message="Loading Ivy ... common.dir=${common.dir}"/>
+	
+	    	<taskdef resource="org/apache/ivy/ant/antlib.xml"
+	    	         uri="antlib:org.apache.ivy.ant"
+			 classpath="${ivy.jar}:${sigil-ivy-plugin.jar}"/>
+	
+		<taskdef name="sigil.bundle"
+			 classname="org.cauldron.bld.ant.BundleTask"
+			 classpath="${sigil-ivy-plugin.jar}"/>
+    	
+    	<taskdef name="sigil.bundle.info"
+    		 classname="org.cauldron.bld.ant.BundleInfoTask"
+    		 classpath="${sigil-ivy-plugin.jar}"/>
+    </target>    
+	
+
+    <!-- ================================= 
+          target: build (default target)
+         ================================= -->
+    <target name="build" depends="publish-local, composites" />
+
+    <target name="ident">
+      <echo message="${ant.project.name}"/>
+    </target>
+
+    <!-- ================================= 
+          target: resolve              
+         ================================= -->
+    <target name="resolve" depends="load-ivy"
+    	description="--> resolve and retrieve dependencies with ivy">
+	<mkdir dir="${deps.dir}"/>
+    	<ivy:resolve file="${ivy.file}" log="${resolve.log}"/>
+    	<ivy:retrieve pattern="${deps.dir}/[artifact].[ext]"
+		symlink="true" sync="true"/>
+    	<!-- sync=true removes empty deps dir, so re-create it -->
+	<mkdir dir="${deps.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: publish-local
+         ================================= -->
+
+    <target name="publish-check" unless="bundle.modified">
+      <ivy:info file="${ivy.file}"/>
+      <condition property="bundle.modified">
+	<not>
+	  <available file="${repository.dir}/local/${ivy.module}" type="dir"/>
+	</not>
+      </condition>
+    </target>
+
+    <target name="publish-local" depends="bundle, publish-check" if="bundle.modified"
+    	description="--> publish project in the local repository">
+
+	<tstamp>
+	    <format property="now" pattern="yyyyMMddHHmmss"/>
+	</tstamp>
+        <property name="local-version" value="${now}"/>
+
+	<antcall target="clean-local"/>
+    	<ivy:publish artifactspattern="${build.lib.dir}/[artifact].[ext]" 
+		       resolver="local"
+		       pubrevision="${local-version}" 
+		       pubdate="${now}"
+		       forcedeliver="true"
+		       status="integration"/>
+        <echo message="project ${ant.project.name} published locally with version ${local-version}" />
+    </target>
+
+    <!-- ================================= 
+          target: report              
+         ================================= -->
+    <target name="report" depends="resolve"
+    	description="--> generates a report of dependencies">
+        <ivy:report todir="${build.dir}"/>
+    </target>
+    
+    <!-- ================================= 
+          target: compile              
+         ================================= -->
+    <target name="compile" depends="resolve"
+    	description="--> compile the project">
+        <!-- uncomment the following to debug classpath -->
+        <!--pathconvert property="cp" refid="javac.classpath"/>
+        <echo>Classpath - ${cp}</echo-->
+        <mkdir dir="${classes.dir}" />
+        <javac srcdir="${src.dir}"
+		destdir="${classes.dir}"
+		classpathref="javac.classpath"
+		target="1.5"
+		debug="true" />
+    </target>
+    
+    <!-- ================================= 
+          target: bundle
+         ================================= -->
+    <target name="bundle" depends="compile"
+    	description="--> build OSGi bundle(s) for this project">
+	<mkdir dir="${build.lib.dir}"/>
+        <sigil.bundle
+                classpathref="sigil.classpath"
+		destpattern="${build.lib.dir}/[name].[ext]"
+		force="${bundle.force}"
+		property="bundle.modified" />
+    </target>
+
+    <!-- ================================= 
+          target: composites              
+         ================================= -->
+    <available file="${composite.dir}" type="dir"
+               property="composite.dir.present"/>
+
+    <target name="composites" if="composite.dir.present"
+    	description="--> filter xml composites replacing ${VERSION} etc.">
+	<mkdir dir="${build.etc.dir}"/>
+	<copy todir="${build.etc.dir}">
+	  <fileset dir="${composite.dir}">
+	    <exclude name="*-template.composite"/>
+	    <include name="*.composite"/>
+	    <include name="*.system"/>
+	  </fileset>
+	  <filterset begintoken="@" endtoken="@">
+	    <filter token="VERSION" value="${bundle.version}"/>
+	    <filter token="COMMUNITY_VERSION" value="${community.version}"/>
+	    <filter token="JINI_VERSION" value="${jini.version}"/>
+	    <filter token="PAREMUS_VERSION" value="${paremus.version}"/>
+	  </filterset>
+	</copy>
+    </target>
+
+    <!-- ================================= 
+          target: install              
+	  install-bin, install-etc deprecated in favour of single
+	  assemble/bin, assemble/etc dirs
+         ================================= -->
+    <target name="install"
+    	 depends="install-lib, install-composites"/>
+
+    <available file="bin" type="dir"
+               property="bin.dir.present"/>
+    <available file="etc" type="dir"
+               property="etc.dir.present"/>
+
+    <target name="install-bin" if="bin.dir.present">
+	<mkdir dir="${install.dir}/bin"/>
+	<copy todir="${install.dir}/bin">
+	  <fileset dir="bin" />
+	</copy>
+	<chmod dir="${install.dir}/bin" perm="755" excludes="*.bat"/>
+    </target>
+
+    <target name="install-etc" if="etc.dir.present">
+	<mkdir dir="${install.dir}/etc"/>
+	<copy todir="${install.dir}/etc">
+	  <fileset dir="etc" />
+	</copy>
+    </target>
+
+    <target name="composites-available">
+      <available file="${build.etc.dir}" type="dir"
+               property="build.etc.dir.present"/>
+    </target>
+
+    <target name="install-composites"
+	 depends="composites, composites-available"
+	      if="build.etc.dir.present">
+	<copy todir="${install.dir}/etc">
+	  <fileset dir="${build.etc.dir}" />
+	</copy>
+    </target>
+
+    <target name="libs-available">
+      <available file="${build.lib.dir}" type="dir"
+               property="build.lib.dir.present"/>
+    </target>
+
+    <target name="install-lib"
+	 depends="publish-local, libs-available"
+	      if="build.lib.dir.present">
+	<copy todir="${install.dir}/lib">
+	  <fileset dir="${build.lib.dir}" />
+	  <mapper type="glob" from="*.jar" to="*-${buildVersion}.jar"/>
+	</copy>
+    </target>
+
+    <!-- ================================= 
+	  target: clean-local              
+	 ================================= -->
+    <target name="clean-local" depends="load-ivy"
+    	description="--> cleans the local repository for the current module">
+       <ivy:info file="${ivy.file}"/>
+       <delete dir="${repository.dir}/local/${ivy.module}"/>
+    </target>
+
+    <!-- ================================= 
+	  target: clean-deps              
+	 ================================= -->
+    <target name="clean-deps"
+    	description="--> clean the project dependencies directory">
+	<delete includeemptydirs="true" dir="${deps.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean-build              
+         ================================= -->
+    <target name="clean-build"
+    	description="--> clean the project built files">
+        <delete includeemptydirs="true" dir="${build.dir}"/>
+    </target>
+
+    <!-- ================================= 
+          target: clean              
+         ================================= -->
+    <target name="clean" depends="ident, clean-build, clean-deps"
+    	description="--> clean the project" />
+
+
+    <!-- ================================= 
+          target: buildlist
+         ================================= -->
+    <target name="buildlist" depends="load-ivy">
+      <ivy:buildlist reference="ordered-list"
+		  onMissingDescriptor="skip">
+	<fileset refid="my.projects"/>
+      </ivy:buildlist>
+    </target>
+
+    <!-- ================================= 
+          target: build-list
+         ================================= -->
+    <target name="build-list" depends="buildlist" 
+	  description="--> build all projects in the right order">
+      <property name="target" value="build"/>
+      <subant target="${target}" buildpathref="ordered-list">
+	<propertyset>
+	  <propertyref name="ivy.loaded" />
+	</propertyset>
+      </subant>
+    </target>
+
+    <!-- ================================= 
+          target: install-list
+         ================================= -->
+    <target name="install-list"
+	  description="--> install all projects">
+      <antcall target="build-list">
+        <param name="target" value="install"/>
+      </antcall>
+    </target>
+
+    <!-- ================================= 
+          target: ident-list
+         ================================= -->
+    <target name="ident-list"
+	  description="--> identify projects">
+      <antcall target="build-list">
+        <param name="target" value="ident"/>
+      </antcall>
+    </target>
+
+    <!-- ================================= 
+          target: clean-list
+         ================================= -->
+    <target name="clean-list"
+	  description="--> clean all projects">
+      <antcall target="build-list">
+        <param name="target" value="clean"/>
+      </antcall>
+    </target>
+
+    <!-- ================================= 
+          target: clean-all
+         ================================= -->
+    <target name="clean-all" depends="clean-list, clean" 
+    description="--> clean repository, cache, and all projects">
+      <delete dir="${repository.dir}"/>
+      <delete dir="${cache.dir}"/>
+      <delete dir="${install.dir}"/>
+      <delete dir="${dist.dir}"/>
+    </target>
+	
+    <target name="sigil.test" depends="ivy-taskdefs">
+        <mkdir dir="${dist.dir}/lib/sigil" />
+        <copy todir="${dist.dir}/lib/sigil" overwrite="true">
+                <fileset dir="${common.dir}/test/lib">
+                        <include name="*.jar" />
+                </fileset>
+        </copy>
+
+        <mkdir dir="${dist.dir}/etc/sigil/boot" />
+
+        <copy todir="${dist.dir}/etc/sigil/boot" overwrite="true">
+                <fileset dir="${common.dir}/test/etc/boot">
+                        <include name="*" />
+                </fileset>
+        </copy>
+
+    	<property name="install.script" value="${dist.dir}/etc/sigil/boot/2-install-tests" />
+        <delete file="${install.script}" />
+
+        <for param="project">
+            <fileset refid="my.projects" />
+            <sequential>
+                <antcall target="sigil.test.setup">
+					<param name="project" value="@{project}"/>
+                </antcall>
+            </sequential>
+        </for>
+
+        <for param="project">
+            <fileset refid="my.projects" />
+            <sequential>
+                <antcall target="sigil.test.install">
+					<param name="project" value="@{project}"/>
+                </antcall>
+            </sequential>
+        </for>
+    	
+        <delete dir="${dist.dir}/../test-results" />
+    	
+        <exec executable="sh" osfamily="unix">
+          <arg line="${dist.dir}/bin/container equinox -verbose=true" />
+          <arg line="-bootScript=etc/sigil/boot" />
+        </exec>
+    </target>	
+
+	<target name="sigil.test.setup">
+		<property name="dir" location="${project}/../" />
+		<path id="test.resources">
+	        <fileset dir="${dir}">
+	        	<include name="build/lib/*.jar" />
+	        	<exclude name="**/*-dl.jar" />
+	        </fileset>
+            <fileset dir="${dir}">
+            	<include name="build/deps/*.jar" />
+	        	<exclude name="**/*!*.jar" />
+            </fileset>
+		</path>
+        <for param="jar">
+        	<path refid="test.resources"/>
+            <sequential>
+            	<antcall target="sigil.test.load">
+                	<param name="jar" value="@{jar}" />
+            	</antcall>
+            </sequential>
+        </for>
+	</target>
+	
+	<target name="sigil.test.install">
+		<property name="dir" location="${project}/../" />
+		<path id="test.bundles">
+	        <fileset dir="${dir}">
+	        	<include name="build/lib/*test.jar" />
+            </fileset>
+		</path>
+        <for param="jar">
+        	<path refid="test.bundles"/>
+            <sequential>
+            	<antcall target="sigil.test.start">
+                	<param name="jar" value="@{jar}" />
+            	</antcall>
+            </sequential>
+        </for>
+	</target>
+	
+	<target name="sigil.test.load">
+		<sigil.bundle.info bundle="${jar}" header="Bundle-SymbolicName" property="test.bundle.symbolic.name"/>
+		<if>
+			<isset property="test.bundle.symbolic.name"/>
+			<then>
+		        <echo file="${install.script}" append="true">cds load boot ${test.bundle.symbolic.name}.jar ${jar}
+</echo>
+			</then>
+		</if>
+	</target>
+	
+	<target name="sigil.test.start">
+		<sigil.bundle.info bundle="${jar}" header="Bundle-SymbolicName" property="test.bundle.symbolic.name"/>
+		<if>
+			<isset property="test.bundle.symbolic.name"/>
+			<then>
+				<sigil.bundle.info bundle="${jar}" header="Bundle-Version" property="test.bundle.version" defaultvalue="0"/>
+				<sigil.bundle.info bundle="${jar}" header="Fragment-Host" property="test.bundle.fragment"/>
+				<if>
+					<isset property="test.bundle.fragment" />
+					<then>
+						<propertyregex property="test.bundle.fragment.name"
+						              input="${test.bundle.fragment}"
+						              regexp="([^;]*).*"
+									  select="\1"
+						              casesensitive="false" />
+
+		        		<echo file="${install.script}" append="true">nim policy -a osgi.installed.bundle/${test.bundle.fragment.name} osgi.fragment.bundle/${test.bundle.symbolic.name}:${test.bundle.version}
+</echo>
+		        		<echo file="${install.script}" append="true">nim add ${test.bundle.symbolic.name}:${test.bundle.version}@fragment
+</echo>
+					</then>
+					<else>
+		        		<echo file="${install.script}" append="true">nim add ${test.bundle.symbolic.name}:${test.bundle.version}@installed
+</echo>
+					</else>
+				</if>
+			</then>
+		</if>
+	</target>
+</project>
diff --git a/sigil/bldcommon/ivysettings.xml b/sigil/bldcommon/ivysettings.xml
new file mode 100644
index 0000000..2ce1415
--- /dev/null
+++ b/sigil/bldcommon/ivysettings.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<ivysettings>
+  <properties file="${ivy.settings.dir}/build.properties"/>
+  <caches defaultCacheDir="${cache.dir}" />
+
+  <settings defaultResolver="local" circularDependencyStrategy="error" />
+
+  <!--
+  <classpath file="${sigil-ivy-plugin.jar}" />
+  Ant tasks "subant" and "ant" cause IvySettings to be re-loaded, 
+  which then re-loads SigilParser in a new URLClassLoader,
+  which causes ProjectRepository to be re-initialised. Outch!
+  Loading sigil-ivy-plugin.jar in load-ivy task, uses the same ClassLoader
+  and thus avoids re-initialising ProjectRepositiory.
+  Search for Derek Baum in the October 2008 ant-dev archives for my attempt
+  at getting the Ivy communiuty to fix this.
+  http://mail-archives.apache.org/mod_mbox/ant-dev/200810.mbox/browser
+  -->
+  <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+  <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+  <parsers>
+    <sigil-parser config="${ivy.settings.dir}/sigil-repos.properties"
+    		quiet="true"/>
+  </parsers>
+
+  <resolvers>
+    <sigil name="sigil"
+	   config="${ivy.settings.dir}/sigil-repos.properties"
+	   extractBCP="true"/>
+
+    <filesystem name="local">
+      <ivy pattern="${repository.dir}/local/${repository.pattern}" />
+      <artifact pattern="${repository.dir}/local/${repository.pattern}" />
+    </filesystem>
+
+    <filesystem name="shared">
+      <ivy pattern="${repository.dir}/shared/${repository.pattern}" />
+      <artifact pattern="${repository.dir}/shared/${repository.pattern}" />
+    </filesystem>
+  </resolvers>
+
+  <modules>
+    <module organisation="sigil" resolver="sigil"/>
+  </modules>
+</ivysettings>
diff --git a/sigil/bldcommon/sigil-repos.properties b/sigil/bldcommon/sigil-repos.properties
new file mode 100644
index 0000000..ace83cc
--- /dev/null
+++ b/sigil/bldcommon/sigil-repos.properties
@@ -0,0 +1,24 @@
+# sigil repository config
+
+# repository config
+
+-repositories:  system, project, bld-common
+
+system;provider:        system
+system;level:           -1
+
+project;provider:       project
+project;level:  0
+project;pattern:        ${..}/**/[sigilproject]
+
+bld-common;provider:	filesystem
+bld-common;level:	1
+bld-common;recurse:	true
+bld-common;dir:		${.}/lib
+
+spring;provider:        obr
+spring;level:           2
+spring;url:             http://sigil.codecauldron.org/spring-repository.obr
+spring;index:           ../build/spring-repository.obr
+
+# end
diff --git a/sigil/build.xml b/sigil/build.xml
new file mode 100644
index 0000000..04f030e
--- /dev/null
+++ b/sigil/build.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="Sigil" default="build">
+
+  <target name="build-eclipse">
+    <subant buildpath="sigil-builder" target="build"/>
+  </target>
+
+  <target name="build-ivy">
+    <subant buildpath="bld-ivy" target="dist"/>
+  </target>
+
+  <target name="build-junit">
+    <subant buildpath="bld-junit" target="install"/>
+    <subant buildpath="bld-junit-cli" target="install"/>
+  </target>
+
+  <target name="clean">
+    <delete dir="build" />
+    <subant buildpath="sigil-builder" target="clean"/>
+    <subant buildpath="bld-ivy" target="clean"/>
+    <subant buildpath="bld-junit" target="clean"/>
+    <subant buildpath="bld-junit-cli" target="clean"/>
+  </target>
+
+  <target name="build" depends="build-eclipse, build-ivy, build-junit" /> 
+
+  <target name="ci-commit" depends="clean, build"/>
+
+  <target name="ci-daily" depends="clean, build">
+    <subant buildpath="sigil-builder" target="new.updateSite"/>
+  </target>
+
+</project>
diff --git a/sigil/misc/do_notice b/sigil/misc/do_notice
new file mode 100644
index 0000000..933e145
--- /dev/null
+++ b/sigil/misc/do_notice
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+here=$(dirname $0)
+
+find . \
+    ! \( -type d \( -name .svn -o -name build \) -prune \) \
+    ! -name '*.jar'  ! -name .classpath ! -name .project \
+    ! -name '*.gif'  ! -name '*.ico' ! -name '*.png' \
+    ! -name '*.config' ! -name '*.policy' \
+    ! -name '*.swp' ! -name '*.txt' ! -name '*.properties' \
+    -type f |
+xargs $here/notice.pl $*
+
diff --git a/sigil/misc/notice.pl b/sigil/misc/notice.pl
new file mode 100755
index 0000000..64b40e9
--- /dev/null
+++ b/sigil/misc/notice.pl
@@ -0,0 +1,295 @@
+#!/usr/bin/env perl
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+use FileHandle;
+
+my $dryrun = 0;
+my $quiet = 0;
+
+#
+# Usage: notice.pl [-n] [-q] files...
+#
+
+my $NOTICE = <<'EOT';
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+EOT
+
+# Note: source code notices must not be changed by code formatter
+
+my $NOTICE_java = $NOTICE;
+$NOTICE_java =~ s/^/ * /gm;
+$NOTICE_java = "/*\n" . $NOTICE_java . " */\n\n";
+
+my $NOTICE_xml = $NOTICE;
+$NOTICE_xml =~ s/^/  /gm;
+$NOTICE_xml = "<!--\n" . $NOTICE_xml . "-->\n";
+
+my $NOTICE_sh = $NOTICE;
+$NOTICE_sh =~ s/^/# /gm;
+
+my $NOTICE_bat = $NOTICE;
+$NOTICE_bat =~ s/^/:: /gm;
+
+sub addNotice {
+    my ($fh, $file, $notice) = @_;
+    print "$file\n";
+
+    return if ($dryrun);
+
+    my $tmp = "$file.tmp";
+    my $fh2 = new FileHandle(">$tmp");
+
+    print $fh2 $notice;
+    while (<$fh>) {
+	print $fh2 $_;
+    }
+
+    unlink($file);
+    rename($tmp, $file);
+}
+
+#
+# locate existing copyright & license
+#	starts at top of file
+#	ends at "package" statement
+#
+sub notice_java {
+    my $file = shift;
+    my $fh = new FileHandle($file);
+    my $header;
+    my $package = undef;
+
+    while (<$fh>) {
+        if (/^package\s+[\w.]+\s*;/) {
+	    $package = $_;
+	    last;
+	}
+	last if ($. > 200);
+	$header .= $_;
+    }
+
+    if (! $package) {
+        print STDERR "$file: ERROR: no package statement.\n";
+	return;
+    }
+
+    return if ($header eq $NOTICE_java);
+
+    # completely replace all before package statement
+    # so DON'T change below, without changing above
+    # to stop at end-of-first-comment.
+    $header = '';
+    #$header = '' if ($header =~ /copyright/im);
+    #$header = "\n" . $header unless ($header =~ /^\n/);
+
+    addNotice($fh, $file, $NOTICE_java . $header . $package);
+}
+
+sub notice_xml {
+    my $file = shift;
+    my $fh = new FileHandle($file);
+    my $header = undef;
+    my $decl = qq(<?xml version="1.0"?>\n);
+    my $end = 0;
+    my $start = 0;
+
+    while (<$fh>) {
+    	if ($. == 1 && /^\<\?/) {
+	    $decl = $_;
+	    next;
+	}
+
+	$header .= $_;
+
+    	if (!$start) {
+	  last unless (/^<!--/);
+	  $start = 1;
+	}
+
+        if (/-->/) {
+	    $end = 1;
+	    last;
+	}
+    }
+
+    return if ($header eq $NOTICE_xml);
+
+    $header = '' if ($header =~ /copyright/im);
+
+    if ($start && !$end) {
+        print STDERR "$file: ERROR: initial comment not terminated.\n";
+	return;
+    }
+
+    addNotice($fh, $file, $decl . $NOTICE_xml . $header);
+}
+
+sub notice_sh {
+    my $file = shift;
+    my $fh = new FileHandle($file);
+    my $header = undef;
+    my $hashbang = undef;
+    my $end = '';
+    my $start = '';
+
+    while (<$fh>) {
+    	if ($. == 1 && /^#!/) {
+	    $hashbang = $_;
+	    next;
+	}
+
+    	if (!$start) {
+	  unless (/^(#|\s*$)/) {
+	    $header = $_;
+	    last;
+	  }
+	  $start = 1;
+	  next if (/^\s*$/);
+	}
+
+        if (!/^#/) {
+	    $end = $_;
+	    last;
+	}
+
+	$header .= $_;
+    }
+
+    return if ($header eq $NOTICE_sh);
+
+    $hashbang .= "\n" if ($hashbang);
+
+    $header = '' if ($header =~ /copyright/im);
+    $header .= $end;
+    $header = "\n" . $header unless ($header =~ /^\n/);
+
+    if ($start && !$end) {
+        print STDERR "$file: ERROR: initial comment not terminated.\n";
+	return;
+    }
+
+    addNotice($fh, $file, $hashbang . $NOTICE_sh . $header);
+}
+
+sub notice_bat {
+    my $file = shift;
+    my $fh = new FileHandle($file);
+    my $header = undef;
+    my $atecho = undef;
+    my $end = '';
+    my $start = '';
+
+    while (<$fh>) {
+	s/\r\n$/\n/;
+    	if ($. == 1 && /^\@echo/) {
+	    $atecho = $_;
+	    next;
+	}
+
+    	if (!$start) {
+	  unless (/^(::|\s*$)/) {
+	    $header = $_;
+	    last;
+	  }
+	  $start = 1;
+	  next if (/^\s*$/);
+	}
+
+        if (!/^::/) {
+	    $end = $_;
+	    last;
+	}
+
+	$header .= $_;
+    }
+
+    return if ($header eq $NOTICE_bat);
+
+    $atecho .= "\n" if ($atecho);
+
+    $header = '' if ($header =~ /copyright/im);
+    $header .= $end;
+    $header = "\n" . $header unless ($header =~ /^\n/);
+
+    if ($start && !$end) {
+        print STDERR "$file: ERROR: initial comment not terminated.\n";
+	return;
+    }
+
+    $header = $atecho . $NOTICE_bat . $header;
+    $header =~ s/\n/\r\n/mg;
+    addNotice($fh, $file, $header);
+}
+
+sub notice_unknown {
+    my $file = shift;
+    my $fh = new FileHandle($file);
+
+    my $line = <$fh>;
+
+    if ($line =~ /^#!/) {
+        notice_sh($file);
+    }
+    elsif ($line =~ /^\<\?xml/) {
+        notice_xml($file);
+    }
+    elsif (! $quiet) {
+	print STDERR "$file: unknown file type.\n";
+    }
+}
+
+foreach my $f (@ARGV) {
+    if ($f eq '-n') {
+	$dryrun++;
+    }
+    elsif ($f eq '-q') {
+	$quiet++;
+    }
+    elsif ($f =~ /\.java$/) {
+	notice_java($f);
+    }
+    elsif ($f =~ /\.(xml|xsd|system|composite)$/) {
+	notice_xml($f);
+    }
+    elsif ($f =~ /\.(sh|app|ini|xargs)$/) {
+	notice_sh($f);
+    }
+    elsif ($f =~ /\.(bat)$/) {
+	notice_bat($f);
+    }
+    else {
+	notice_unknown($f);
+    }
+}
+
diff --git a/sigil/org.cauldron.bld.core.tests/.classpath b/sigil/org.cauldron.bld.core.tests/.classpath
new file mode 100644
index 0000000..466e08f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/org.cauldron.bld.core"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.core.tests/.project b/sigil/org.cauldron.bld.core.tests/.project
new file mode 100644
index 0000000..44f71df
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.bld.core.tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java
new file mode 100644
index 0000000..b4bfc51
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.util.Arrays;
+
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.common.VersionRange;
+
+import junit.framework.TestCase;
+
+public class BundleModelElementTest extends TestCase {
+
+	public BundleModelElementTest(String name) {
+		super(name);
+	}
+	
+	public void testAddRequires() {
+		BundleModelElement element = new BundleModelElement();
+		checkRequires(element);
+	}
+	
+	public void testAddImports() {
+		BundleModelElement element = new BundleModelElement();
+		checkImports(element);
+	}
+	
+	public void testAddImportsAndRequires() {
+		BundleModelElement element = new BundleModelElement();
+		checkImports(element);
+		checkRequires(element);
+		
+		element = new BundleModelElement();
+		checkRequires(element);
+		checkImports(element);
+	}
+	
+	private void checkImports(BundleModelElement element) {
+		PackageImport foo = new PackageImport();
+		foo.setPackageName("foo");
+		foo.setVersions( VersionRange.parseVersionRange("1.0.0") );
+		PackageImport bar = new PackageImport();
+		bar.setPackageName( "bar" );
+		bar.setVersions( VersionRange.parseVersionRange("[2.2.2, 3.3.3]") );
+		PackageImport baz = new PackageImport();
+		baz.setPackageName( "baz" );
+		baz.setVersions( VersionRange.parseVersionRange("[3.0.0, 4.0.0)") );
+		
+		element.addChild(foo.clone());
+		element.addChild(bar.clone());
+		element.addChild(baz.clone());
+		
+		assertTrue( Arrays.asList(element.children()).contains(foo) );
+		assertTrue( Arrays.asList(element.children()).contains(bar) );
+		assertTrue( Arrays.asList(element.children()).contains(baz) );
+	}
+
+	private void checkRequires(BundleModelElement element) {
+		RequiredBundle foo = new RequiredBundle();
+		foo.setSymbolicName( "foo" );
+		foo.setVersions( VersionRange.parseVersionRange("1.0.0") );
+		RequiredBundle bar = new RequiredBundle();
+		bar.setSymbolicName( "bar" );
+		bar.setVersions( VersionRange.parseVersionRange("[2.2.2, 3.3.3]") );
+		RequiredBundle baz = new RequiredBundle();
+		baz.setSymbolicName( "baz" );
+		baz.setVersions( VersionRange.parseVersionRange("[3.0.0, 4.0.0)") );
+		
+		element.addChild(foo.clone());
+		element.addChild(bar.clone());
+		element.addChild(baz.clone());
+		
+		assertTrue( Arrays.asList(element.children()).contains(foo) );
+		assertTrue( Arrays.asList(element.children()).contains(bar) );
+		assertTrue( Arrays.asList(element.children()).contains(baz) );
+	}
+}
diff --git a/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java
new file mode 100644
index 0000000..080d5ab
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class ConfigTest extends TestCase {
+	
+	static final URI base = URI.create("test/ConfigTest/sigil.properties");
+
+	public ConfigTest(String name) {
+		super(name);
+	}
+	
+	public void testSimple() throws IOException {
+		IBldProject project = BldFactory.getProject(base.resolve("test1.properties"));
+		
+		ISigilBundle bundle = project.getDefaultBundle();
+		IBundleModelElement info = bundle.getBundleInfo();
+		
+		checkImports(info.getImports());
+		
+		//IBundleModelElement requirements = project.getRequirements();
+	}
+	
+	private void checkImports(Set<IPackageImport> imports) {
+		PackageImport foo = new PackageImport();
+		foo.setPackageName("foo");
+		foo.setVersions(VersionRange.parseVersionRange("1.0.0"));
+		PackageImport bar = new PackageImport();
+		bar.setPackageName( "bar" );
+		bar.setVersions(VersionRange.parseVersionRange("[2.2.2, 3.3.3]"));
+		PackageImport baz = new PackageImport();
+		baz.setPackageName( "baz" );
+		baz.setVersions(VersionRange.parseVersionRange("[3.0.0, 4.0.0)"));
+		
+		assertTrue(foo.toString(), imports.contains(foo));
+		assertTrue(bar.toString(), imports.contains(bar));
+		assertTrue(baz.toString(), imports.contains(baz));
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties b/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties
new file mode 100644
index 0000000..043837f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties
@@ -0,0 +1,10 @@
+
+# test1
+
+-bundles: default
+
+-imports: \
+  foo;version=1.0, \
+  bar;version="[2.2.2,3.3.3]", \
+  baz;version="[3.0.0,4.0.0)", \
+
diff --git a/sigil/org.cauldron.bld.core/.classpath b/sigil/org.cauldron.bld.core/.classpath
new file mode 100644
index 0000000..b26935e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="lib" path="lib/bndlib.jar" sourcepath="/bld-ivy/lib/ant/bnd-0.0.312.jar"/>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.core/.project b/sigil/org.cauldron.bld.core/.project
new file mode 100644
index 0000000..3bbec56
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.bld.core</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF b/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..cc6768b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil_bld Plug-in
+Bundle-SymbolicName: org.cauldron.bld.core;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Activator: org.cauldron.bld.core.BldCore
+Bundle-ActivationPolicy: lazy
+Export-Package: org.cauldron.bld.bnd,
+ org.cauldron.bld.config,
+ org.cauldron.bld.core,
+ org.cauldron.bld.core.licence,
+ org.cauldron.bld.core.repository,
+ org.cauldron.sigil.model,
+ org.cauldron.sigil.model.common,
+ org.cauldron.sigil.model.eclipse,
+ org.cauldron.sigil.model.osgi,
+ org.cauldron.sigil.repository
+Import-Package: org.osgi.framework
+Require-Bundle: org.eclipse.equinox.common;bundle-version="3.4.0"
+Bundle-ClassPath: lib/bndlib.jar,
+ .
diff --git a/sigil/org.cauldron.bld.core/build.properties b/sigil/org.cauldron.bld.core/build.properties
new file mode 100644
index 0000000..66f45cb
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               profiles/,\
+               lib/bndlib.jar
diff --git a/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile b/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile
new file mode 100644
index 0000000..1157a29
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.microedition.io
+org.osgi.framework.bootdelegation = \
+ javax.microedition.io
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ CDC-1.0/Foundation-1.0
+osgi.java.profile.name = CDC-1.0/Foundation-1.0
diff --git a/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile b/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile
new file mode 100644
index 0000000..374300a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.microedition.io,\
+ javax.microedition.pki,\
+ javax.security.auth.x500
+org.osgi.framework.bootdelegation = \
+ javax.microedition.io,\
+ javax.microedition.pki,\
+ javax.security.auth.x500
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ CDC-1.0/Foundation-1.0,\
+ CDC-1.1/Foundation-1.1
+osgi.java.profile.name = CDC-1.1/Foundation-1.1
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile
new file mode 100644
index 0000000..28f973a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile
@@ -0,0 +1,42 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ org.omg.CORBA,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextPackage
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ JRE-1.1,\
+ J2SE-1.2
+osgi.java.profile.name = J2SE-1.2
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile
new file mode 100644
index 0000000..b7dbdfa
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile
@@ -0,0 +1,63 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.omg.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3
+osgi.java.profile.name = J2SE-1.3
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile
new file mode 100644
index 0000000..5655989
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile
@@ -0,0 +1,123 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4
+osgi.java.profile.name = J2SE-1.4
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile
new file mode 100644
index 0000000..5c084ea
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile
@@ -0,0 +1,150 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.activity,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = J2SE-1.5
diff --git a/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile b/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile
new file mode 100644
index 0000000..f335899
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile
@@ -0,0 +1,185 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.activation,\
+ javax.activity,\
+ javax.annotation,\
+ javax.annotation.processing,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.jws,\
+ javax.jws.soap,\
+ javax.lang.model,\
+ javax.lang.model.element,\
+ javax.lang.model.type,\
+ javax.lang.model.util,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.script,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.tools,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.bind,\
+ javax.xml.bind.annotation,\
+ javax.xml.bind.annotation.adapters,\
+ javax.xml.bind.attachment,\
+ javax.xml.bind.helpers,\
+ javax.xml.bind.util,\
+ javax.xml.crypto,\
+ javax.xml.crypto.dom,\
+ javax.xml.crypto.dsig,\
+ javax.xml.crypto.dsig.dom,\
+ javax.xml.crypto.dsig.keyinfo,\
+ javax.xml.crypto.dsig.spec,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.soap,\
+ javax.xml.stream,\
+ javax.xml.stream.events,\
+ javax.xml.stream.util,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.ws,\
+ javax.xml.ws.handler,\
+ javax.xml.ws.handler.soap,\
+ javax.xml.ws.http,\
+ javax.xml.ws.soap,\
+ javax.xml.ws.spi,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5,\
+ JavaSE-1.6
+osgi.java.profile.name = JavaSE-1.6
diff --git a/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile
new file mode 100644
index 0000000..d8c2d3f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = 
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0
+osgi.java.profile.name = OSGi/Minimum-1.0
diff --git a/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile
new file mode 100644
index 0000000..2d8b661
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = 
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1
+osgi.java.profile.name = OSGi/Minimum-1.1
diff --git a/sigil/org.cauldron.bld.core/profiles/profile.list b/sigil/org.cauldron.bld.core/profiles/profile.list
new file mode 100644
index 0000000..bea1b25
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/profile.list
@@ -0,0 +1,21 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+# 
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+java.profiles = \
+ JavaSE-1.6.profile,\
+ J2SE-1.5.profile,\
+ J2SE-1.4.profile,\
+ J2SE-1.3.profile,\
+ J2SE-1.2.profile,\
+ JRE-1.1.profile,\
+ CDC-1.1_Foundation-1.1.profile,\
+ CDC-1.0_Foundation-1.0.profile,\
+ OSGi_Minimum-1.0.profile,\
+ OSGi_Minimum-1.1.profile,\
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java
new file mode 100644
index 0000000..6f6a33e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java
@@ -0,0 +1,800 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.bnd;
+
+import static java.lang.String.format;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.jar.Attributes;
+
+import org.cauldron.bld.config.BldAttr;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.repository.SystemRepositoryProvider;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+import aQute.lib.osgi.Builder;
+import aQute.lib.osgi.Constants;
+import aQute.lib.osgi.Jar;
+import aQute.lib.osgi.Processor;
+
+public class BundleBuilder {
+    public static final String COMPONENT_ACTIVATOR_PKG = "XXX-FIXME-XXX";
+    public static final String COMPONENT_ACTIVATOR = COMPONENT_ACTIVATOR_PKG + ".Activator";
+    public static final String[] COMPONENT_ACTIVATOR_DEPS = { "XXX-FIXME-XXX",
+            "org.osgi.framework", "org.osgi.util.tracker" };
+    public static final String COMPONENT_DIR = "META-INF/XXX-FIXME-XXX";
+    public static final String COMPONENT_FLAG = "Installable-Component";
+    public static final String COMPONENT_LIST = "Installable-Component-Templates";
+
+    private IBldProject project;
+    private File[] classpath;
+    private String destPattern;
+    private Properties env;
+    private List<String> errors = new ArrayList<String>();
+    private List<String> warnings = new ArrayList<String>();
+
+    private Set<String> unused = new HashSet<String>();
+    private String lastBundle = null;
+
+    private boolean addMissingImports;
+    private boolean omitUnusedImports;
+    private String defaultPubtype;
+    private String codebaseFormat;
+    private Set<String> systemPkgs;
+
+    public interface Log {
+        void warn(String msg);
+
+        void verbose(String msg);
+    }
+
+    /**
+     * creates a BundleBuilder.
+     * 
+     * @param classpath
+     * @param destPattern
+     *            ivy-like pattern: PATH/[id].[ext] [id] is replaced with the
+     *            bundle id. [name] is replaced with the Bundle-SymbolicName
+     *            [ext] is replaced with "jar".
+     * @param hashtable
+     */
+    public BundleBuilder(IBldProject project, File[] classpath, String destPattern, Properties env) {
+        this.project = project;
+        this.classpath = classpath;
+        this.destPattern = destPattern;
+        this.env = env;
+
+        Properties options = project.getOptions();
+
+        addMissingImports = options.containsKey(BldAttr.OPTION_ADD_IMPORTS)
+                && Boolean.parseBoolean(options.getProperty(BldAttr.OPTION_ADD_IMPORTS));
+        omitUnusedImports = options.containsKey(BldAttr.OPTION_OMIT_IMPORTS)
+                && Boolean.parseBoolean(options.getProperty(BldAttr.OPTION_OMIT_IMPORTS));
+
+        defaultPubtype = options.getProperty(BldAttr.PUBTYPE_ATTRIBUTE, "rmi.codebase");
+
+        codebaseFormat = options.getProperty("codebaseFormat",
+                "cds://%1$s?bundle.symbolic.name=%2$s&type=%3$s");
+
+        for (IBldBundle b : project.getBundles()) {
+            lastBundle = b.getId();
+            for (IPackageImport import1 : b.getImports()) {
+                if (import1.getOSGiImport().equals(IPackageImport.OSGiImport.AUTO)) {
+                    unused.add(import1.getPackageName());
+                }
+            }
+        }
+
+        try {
+            systemPkgs = new HashSet<String>();
+            Properties profile = SystemRepositoryProvider.readProfile(null);
+            String pkgs = profile.getProperty("org.osgi.framework.system.packages");
+            for (String pkg : pkgs.split(",\\s*")) {
+                systemPkgs.add(pkg);
+            }
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    public List<String> errors() {
+        return errors;
+    }
+
+    public List<String> warnings() {
+        return warnings;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void convertErrors(String prefix, List messages) {
+        // TODO: make error mapping more generic
+        final String jarEmpty = "The JAR is empty";
+
+        for (Object omsg : messages) {
+            if (jarEmpty.equals(omsg))
+                warnings.add(prefix + omsg);
+            else
+                errors.add(prefix + omsg);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void convertWarnings(String prefix, List messages) {
+        for (Object omsg : messages) {
+            warnings.add(prefix + omsg);
+        }
+    }
+
+    public boolean createBundle(IBldBundle bundle, boolean force, Log log) throws Exception {
+        int bracket = destPattern.indexOf('[');
+        if (bracket < 0) {
+            throw new Exception("destPattern MUST contain [id] or [name].");
+        }
+
+        String dest = destPattern.replaceFirst("\\[id\\]", bundle.getId());
+        dest = dest.replaceFirst("\\[name\\]", bundle.getSymbolicName());
+        dest = dest.replaceFirst("\\[ext\\]", "jar");
+
+        bracket = dest.indexOf('[');
+        if (bracket >= 0) {
+            String token = dest.substring(bracket);
+            throw new Exception("destPattern: expected [id] or [name]: " + token);
+        }
+
+        errors.clear();
+        warnings.clear();
+
+        if (!bundle.getDownloadContents().isEmpty()) {
+            // create dljar
+            Properties dlspec = new Properties();
+            StringBuilder sb = new StringBuilder();
+
+            for (String pkg : bundle.getDownloadContents()) {
+                if (sb.length() > 0)
+                    sb.append(",");
+                sb.append(pkg);
+            }
+
+            dlspec.setProperty(Constants.PRIVATE_PACKAGE, sb.toString());
+            dlspec.setProperty(Constants.BUNDLE_NAME, "Newton download jar");
+            dlspec.setProperty(Constants.NOEXTRAHEADERS, "true");
+            // stop it being a bundle, so cds doesn't scan it
+            dlspec.setProperty(Constants.REMOVE_HEADERS, Constants.BUNDLE_SYMBOLICNAME);
+
+            Builder builder = new Builder();
+            builder.setProperties(dlspec);
+            builder.setClasspath(classpath);
+
+            Jar dljar = builder.build();
+            convertErrors("BND (dljar): ", builder.getErrors());
+            convertWarnings("BND (dljar): ", builder.getWarnings());
+
+            String dldest = dest.replaceFirst("\\.jar$", "-dl.jar");
+            File dloutput = new File(dldest);
+            if (!dloutput.exists() || dloutput.lastModified() <= dljar.lastModified() || force) {
+                // jar.write(dldest) catches and ignores IOException
+                OutputStream out = new FileOutputStream(dldest);
+                dljar.write(out);
+                out.close();
+                dljar.close();
+                // XXX deleting dljar causes it to be rebuilt each time
+                // XXX but leaving it mean it may be installed where it's not
+                // wanted/needed.
+                dloutput.deleteOnExit();
+            }
+            builder.close();
+        }
+
+        Properties spec = getBndSpec(bundle, dest);
+
+        if (log != null) {
+            log.verbose("BND instructions: " + spec.toString());
+        }
+
+        Builder builder = new Builder();
+        builder.setPedantic(true);
+        builder.setProperties(spec);
+        builder.mergeProperties(env, false);
+
+        builder.setClasspath(classpath);
+        // builder.setSourcepath(sourcepath);
+
+        Jar jar = builder.build();
+
+        convertErrors("BND: ", builder.getErrors());
+        convertWarnings("BND: ", builder.getWarnings());
+
+        augmentImports(builder, jar, bundle);
+
+        if (log != null) {
+            for (String warn : warnings) {
+                log.warn(warn);
+            }
+        }
+
+        if (!errors.isEmpty()) {
+            throw new Exception(errors.toString());
+        }
+
+        boolean modified = false;
+        File output = new File(dest);
+
+        if (!output.exists() || force || (output.lastModified() <= jar.lastModified())
+                || (output.lastModified() <= project.getLastModified())) {
+            modified = true;
+            // jar.write(dest) catches and ignores IOException
+            OutputStream out = new FileOutputStream(dest);
+            jar.write(out);
+            out.close();
+            jar.close();
+        }
+
+        builder.close();
+
+        return modified;
+    }
+
+    private void augmentImports(Builder builder, Jar jar, IBldBundle bundle) throws IOException {
+        Attributes main = jar.getManifest().getMainAttributes();
+        String impHeader = main.getValue(Constants.IMPORT_PACKAGE);
+        Map<String, Map<String, String>> bndImports = Processor.parseHeader(impHeader, builder);
+
+        if (bndImports.isEmpty())
+            return;
+
+        ArrayList<String> self = new ArrayList<String>();
+        ArrayList<String> missing = new ArrayList<String>();
+        ArrayList<String> modified = new ArrayList<String>();
+        ArrayList<String> unversioned = new ArrayList<String>();
+
+        String expHeader = main.getValue(Constants.EXPORT_PACKAGE);
+        Set<String> bndExports = Processor.parseHeader(expHeader, builder).keySet();
+
+        HashMap<String, IPackageImport> imports = new HashMap<String, IPackageImport>();
+        for (IPackageImport pi : getImports(bundle)) {
+            switch (pi.getOSGiImport()) {
+            case NEVER:
+                break;
+            case ALWAYS:
+                String pkg = pi.getPackageName();
+                if (!bndImports.containsKey(pkg)) {
+                    // Bnd doesn't think this import is needed - but we know
+                    // better
+                    HashMap<String, String> attrs = new HashMap<String, String>();
+                    attrs.put(BldAttr.VERSION_ATTRIBUTE, pi.getVersions().toString());
+                    bndImports.put(pkg, attrs);
+                    modified.add(pkg + ";resolve=runtime");
+                }
+                // fall thru */
+            case AUTO:
+                imports.put(pi.getPackageName(), pi);
+                break;
+            }
+        }
+
+        boolean importDot = false;
+
+        for (String pkg : bndImports.keySet()) {
+            unused.remove(pkg);
+            Map<String, String> attrs = bndImports.get(pkg);
+            String currentVersion = (String) attrs.get(BldAttr.VERSION_ATTRIBUTE);
+            IPackageImport pi = imports.get(pkg);
+
+            if (pi != null) {
+                VersionRange range = pi.getVersions();
+                String version = range.toString();
+
+                if (!version.equals(currentVersion) && !range.equals(VersionRange.ANY_VERSION)) {
+                    attrs.put(BldAttr.VERSION_ATTRIBUTE, version);
+                    if (pi.isOptional())
+                        attrs.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
+                    modified
+                            .add(pkg + ";version=" + version + (pi.isOptional() ? ";optional" : ""));
+                } else if ((currentVersion == null) && !systemPkgs.contains(pkg)) {
+                    unversioned.add(pkg);
+                }
+            } else {
+                // bnd added the import ...
+                if (currentVersion == null) {
+                    String defaultVersion = project.getDefaultPackageVersion(pkg);
+                    if (defaultVersion != null) {
+                        attrs.put(BldAttr.VERSION_ATTRIBUTE, defaultVersion);
+                        currentVersion = defaultVersion;
+                    }
+                }
+
+                String imp = pkg + (currentVersion == null ? "" : ";version=" + currentVersion);
+                if (bndExports.contains(pkg)) {
+                    self.add(imp);
+                } else {
+                    if (pkg.equals(".")) {
+                        warnings.add("Bnd wants to import '.' (ignored)");
+                        importDot = true;
+                    } else {
+                        missing.add(imp);
+                    }
+                }
+            }
+        }
+
+        if (!modified.isEmpty() || importDot) {
+            if (importDot)
+                bndImports.remove(".");
+            // warnings.add("INFO: sigil modified imports: " + modified);
+            main.putValue(Constants.IMPORT_PACKAGE, Processor.printClauses(bndImports,
+                    "resolution:"));
+        }
+
+        if (!self.isEmpty()) {
+            // warnings.add("INFO: added self imports: " + self);
+        }
+
+        if (!missing.isEmpty()) {
+            warnings.add("missing imports (added): " + missing);
+        }
+
+        if (!unversioned.isEmpty()) {
+            warnings.add("unversioned imports: " + unversioned);
+        }
+
+        if (bundle.getId().equals(lastBundle)) {
+            if (!unused.isEmpty()) {
+                warnings.add("unused imports (omitted): " + unused);
+            }
+        }
+    }
+
+    public Properties getBndSpec(IBldBundle bundle, String dest) throws IOException {
+        Properties spec = new Properties();
+
+        String junkHeaders = Constants.INCLUDE_RESOURCE; // shows local build
+                                                         // paths; can be
+                                                         // verbose
+        junkHeaders += "," + Constants.PRIVATE_PACKAGE; // less useful, as we
+                                                        // use it for exported
+                                                        // content too.
+
+        spec.setProperty(Constants.REMOVE_HEADERS, junkHeaders);
+        spec.setProperty(Constants.NOEXTRAHEADERS, "true"); // Created-By,
+                                                            // Bnd-LastModified
+                                                            // and Tool
+        spec.setProperty(Constants.CREATED_BY, "sigil.codecauldron.org");
+
+        Properties headers = bundle.getHeaders();
+        // XXX: catch attempts to set headers that conflict with Bnd
+        // instructions we generate?
+        spec.putAll(headers);
+
+        spec.setProperty(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
+        spec.setProperty(Constants.BUNDLE_VERSION, bundle.getVersion());
+
+        String activator = bundle.getActivator();
+        if (activator != null)
+            spec.setProperty(Constants.BUNDLE_ACTIVATOR, activator);
+
+        addRequirements(bundle, spec);
+
+        List<String> exports = addExports(bundle, spec);
+
+        String composites = addResources(bundle, spec);
+
+        ArrayList<String> contents = new ArrayList<String>();
+        contents.addAll(bundle.getContents());
+
+        if (contents.isEmpty()) {
+            if (!project.getSourcePkgs().isEmpty()) {
+                contents.addAll(project.getSourcePkgs());
+            } else {
+                contents.addAll(exports);
+            }
+        }
+
+        if (composites.length() > 0) {
+            if (spec.containsKey(Constants.BUNDLE_ACTIVATOR))
+                warnings.add("-activator ignored when -composites specified.");
+            spec.setProperty(Constants.BUNDLE_ACTIVATOR, COMPONENT_ACTIVATOR);
+            spec.setProperty(COMPONENT_FLAG, "true");
+            spec.setProperty(COMPONENT_LIST, composites);
+            // add activator pkg directly, to avoid needing to add jar to
+            // Bundle-ClassPath.
+            // split-package directive needed to stop Bnd whinging when using
+            // other bundles containing the component-activator.
+            contents.add(COMPONENT_ACTIVATOR_PKG + ";-split-package:=merge-first");
+        }
+
+        List<String> srcPkgs = addLibs(bundle, dest, spec);
+
+        contents.addAll(srcPkgs);
+        addContents(contents, spec);
+
+        IRequiredBundle fh = bundle.getFragmentHost();
+        if (fh != null) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(fh.getSymbolicName());
+            addVersions(fh.getVersions(), sb);
+            spec.setProperty(Constants.FRAGMENT_HOST, sb.toString());
+        }
+
+        return spec;
+    }
+
+    private void addContents(List<String> contents, Properties spec) {
+        // add contents
+        StringBuilder sb = new StringBuilder();
+        for (String pkg : contents) {
+            if (sb.length() > 0)
+                sb.append(",");
+            sb.append(pkg);
+        }
+
+        if (sb.length() > 0)
+            spec.setProperty(Constants.PRIVATE_PACKAGE, sb.toString());
+    }
+
+    private void appendProperty(String key, String value, Properties p) {
+        String list = p.getProperty(key);
+
+        if (list == null) {
+            list = value;
+        } else {
+            list = list + "," + value;
+        }
+
+        p.setProperty(key, list);
+    }
+
+    private List<String> addLibs(IBldBundle bundle, String dest, Properties spec)
+            throws IOException {
+        // final String cleanVersion =
+        // Builder.cleanupVersion(bundle.getVersion());
+        final String name = bundle.getSymbolicName();
+
+        ArrayList<String> srcPkgs = new ArrayList<String>();
+        Map<String, Map<String, String>> libs = bundle.getLibs();
+
+        if (!bundle.getDownloadContents().isEmpty()) {
+            // implicitly add dljar
+            File fdest = new File(dest);
+            String dlname = fdest.getName().replaceFirst("\\.jar$", "-dl.jar");
+
+            HashMap<String, String> attr = new HashMap<String, String>();
+            attr.put(BldAttr.KIND_ATTRIBUTE, "codebase");
+            attr.put(BldAttr.PUBLISH_ATTRIBUTE, dlname);
+
+            HashMap<String, Map<String, String>> lib2 = new HashMap<String, Map<String, String>>();
+            lib2.putAll(libs);
+            lib2.put(dlname, attr);
+            libs = lib2;
+        }
+
+        StringBuilder items = new StringBuilder();
+
+        for (String jarpath : libs.keySet()) {
+            Map<String, String> attr = libs.get(jarpath);
+            String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
+            String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
+
+            // first find the lib ..
+            String path = attr.get(BldAttr.PATH_ATTRIBUTE);
+            if (path == null)
+                path = jarpath;
+
+            File fsPath = bundle.resolve(path);
+
+            if (!fsPath.exists()) {
+                // try destDir
+                File destDir = new File(dest).getParentFile();
+                File file = new File(destDir, fsPath.getName());
+
+                if (!file.exists()) {
+                    // try searching classpath
+                    file = findInClasspathDir(fsPath.getName());
+                }
+
+                if (file != null && file.exists())
+                    fsPath = file;
+            }
+
+            if (!fsPath.exists()) {
+                // XXX: find external bundle using name and version range?
+                // For now just let BND fail when it can't find resource.
+            }
+
+            appendProperty(Constants.INCLUDE_RESOURCE, jarpath + "=" + fsPath, spec);
+
+            if ("classpath".equals(kind)) {
+                String bcp = spec.getProperty(Constants.BUNDLE_CLASSPATH);
+                if (bcp == null || bcp.length() == 0)
+                    spec.setProperty(Constants.BUNDLE_CLASSPATH, ".");
+                appendProperty(Constants.BUNDLE_CLASSPATH, jarpath, spec);
+            }
+
+            if (publish != null) {
+                String pubtype = attr.get(BldAttr.PUBTYPE_ATTRIBUTE);
+                if (pubtype == null)
+                    pubtype = defaultPubtype;
+
+                if ("codebase".equals(kind)) {
+                    String codebase = format(codebaseFormat, publish, name, pubtype);
+                    String zone = attr.get(BldAttr.ZONE_ATTRIBUTE);
+                    if (zone != null)
+                        codebase += "&zone=" + zone;
+                    appendProperty("RMI-Codebase", codebase, spec);
+                }
+
+                // add item to publish xml
+                items.append(format("<item name=\"%s\" path=\"%s\">\n", publish, jarpath));
+                items.append(format("<attribute name=\"type\" value=\"%s\"/>\n", pubtype));
+                items.append("</item>\n");
+            }
+        }
+
+        if (items.length() > 0) {
+            File publishFile = new File(dest.replaceFirst("\\.jar$", "-publish.xml"));
+            publishFile.deleteOnExit();
+            PrintWriter writer = new PrintWriter(new FileWriter(publishFile));
+
+            writer.println("<publish>");
+            writer.println(format("<attribute name=\"bundle.symbolic.name\" value=\"%s\"/>", name));
+            writer.print(items.toString());
+            writer.println("</publish>");
+            writer.close();
+
+            appendProperty(Constants.INCLUDE_RESOURCE, "publish.xml=" + publishFile, spec);
+        }
+
+        return srcPkgs;
+    }
+
+    private String addResources(IBldBundle bundle, Properties spec) {
+        Map<String, String> resources = bundle.getResources();
+        StringBuilder composites = new StringBuilder();
+
+        for (String composite : bundle.getComposites()) {
+            File path = bundle.resolve(composite);
+            String name = path.getName();
+
+            String bPath = COMPONENT_DIR + "/" + name;
+            resources.put(bPath, path.getPath());
+
+            if (composites.length() > 0)
+                composites.append(",");
+            composites.append(bPath);
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (String bPath : resources.keySet()) {
+            if (bPath.startsWith("@")) { // Bnd in-line jar
+                if (sb.length() > 0)
+                    sb.append(",");
+                sb.append('@');
+                sb.append(bundle.resolve(bPath.substring(1)));
+                continue;
+            }
+
+            String fsPath = resources.get(bPath);
+            if ("".equals(fsPath))
+                fsPath = bPath;
+
+            File resolved = bundle.resolve(fsPath);
+
+            // fsPath may contain Bnd variable, making path appear to not exist
+
+            if (!resolved.exists()) {
+                // Bnd already looks for classpath jars
+                File found = findInClasspathDir(fsPath);
+                if (found != null) {
+                    fsPath = found.getPath();
+                } else {
+                    fsPath = resolved.getAbsolutePath();
+                }
+            } else {
+                fsPath = resolved.getAbsolutePath();
+            }
+
+            if (sb.length() > 0)
+                sb.append(",");
+            sb.append(bPath);
+            sb.append('=');
+            sb.append(fsPath);
+        }
+
+        if (sb.length() > 0)
+            spec.setProperty(Constants.INCLUDE_RESOURCE, sb.toString());
+
+        return composites.toString();
+    }
+
+    private List<IPackageImport> getImports(IBldBundle bundle) {
+        List<IPackageImport> imports = bundle.getImports();
+        Set<String> pkgs = new HashSet<String>();
+
+        for (IPackageImport pi : imports) {
+            pkgs.add(pi.getPackageName());
+        }
+
+        // add component activator imports
+        if (!bundle.getComposites().isEmpty()) {
+            for (String pkg : BundleBuilder.COMPONENT_ACTIVATOR_DEPS) {
+                if (pkgs.contains(pkg))
+                    continue;
+                PackageImport pi = new PackageImport();
+                pi.setPackageName(pkg);
+                String versions = project.getDefaultPackageVersion(pkg);
+                if (versions != null)
+                    pi.setVersions(VersionRange.parseVersionRange(versions));
+                imports.add(pi);
+            }
+        }
+
+        return imports;
+    }
+
+    private void addRequirements(IBldBundle bundle, Properties spec) {
+        StringBuilder sb = new StringBuilder();
+
+        // option;addMissingImports=true
+        // Lets Bnd calculate imports (i.e. specify *),
+        // which are then examined by augmentImports();
+
+        // option;omitUnusedImports=true (implies addMissingImports=true)
+        // When project contains multiple bundles which don't all use all
+        // imports,
+        // avoids warnings like:
+        // "Importing packages that are never referred to by any class on the Bundle-ClassPath"
+
+        if (omitUnusedImports && !addMissingImports) {
+            warnings.add("omitUnusedImports ignored as addMissingImports=false.");
+            omitUnusedImports = false;
+        }
+
+        List<IPackageImport> imports = getImports(bundle);
+
+        sb.setLength(0);
+
+        // allow existing header;Package-Import to specify ignored packages
+        sb.append(spec.getProperty(Constants.IMPORT_PACKAGE, ""));
+
+        for (IPackageImport pi : imports) {
+            switch (pi.getOSGiImport()) {
+            case AUTO:
+                if (omitUnusedImports)
+                    continue; // added by Import-Package: * and fixed by
+                              // augmentImports()
+                break;
+            case NEVER:
+                if (pi.isDependency())
+                    continue; // resolve=compile
+                break;
+            case ALWAYS:
+                // Bnd will probably whinge that this import is not used.
+                // we omit it here and replace it in augmentImports,
+                // but only if addMissingImports is true;
+                // otherwise, if the import is used, Bnd will fail.
+                if (addMissingImports)
+                    continue;
+                break;
+            }
+
+            if (sb.length() > 0)
+                sb.append(",");
+
+            if (pi.getOSGiImport().equals(IPackageImport.OSGiImport.NEVER)) {
+                sb.append("!");
+                sb.append(pi.getPackageName());
+            } else {
+                sb.append(pi.getPackageName());
+                addVersions(pi.getVersions(), sb);
+
+                if (pi.isOptional()) {
+                    sb.append(";resolution:=optional");
+                }
+            }
+        }
+
+        if (addMissingImports) {
+            if (sb.length() > 0)
+                sb.append(",");
+            sb.append("*");
+        }
+
+        spec.setProperty(Constants.IMPORT_PACKAGE, sb.toString());
+
+        sb.setLength(0);
+        for (IRequiredBundle rb : bundle.getRequires()) {
+            if (sb.length() > 0)
+                sb.append(",");
+            sb.append(rb.getSymbolicName());
+            addVersions(rb.getVersions(), sb);
+        }
+
+        if (sb.length() > 0) {
+            spec.setProperty(Constants.REQUIRE_BUNDLE, sb.toString());
+        }
+    }
+
+    private List<String> addExports(IBldBundle bundle, Properties spec) {
+        List<IPackageExport> exports = bundle.getExports();
+        ArrayList<String> list = new ArrayList<String>();
+        StringBuilder sb = new StringBuilder();
+
+        for (IPackageExport export : exports) {
+            if (sb.length() > 0)
+                sb.append(",");
+            sb.append(export.getPackageName());
+            if (!export.getVersion().equals(Version.emptyVersion)) {
+                sb.append(";version=\"");
+                sb.append(export.getVersion());
+                sb.append("\"");
+            }
+            list.add(export.getPackageName());
+        }
+
+        if (sb.length() > 0) {
+            // EXPORT_CONTENTS just sets the Export-Package manifest header;
+            // it doesn't add contents like EXPORT_PACKAGE does.
+            spec.setProperty(Constants.EXPORT_CONTENTS, sb.toString());
+        }
+
+        return list;
+    }
+
+    private void addVersions(VersionRange range, StringBuilder sb) {
+        if (!range.equals(VersionRange.ANY_VERSION)) {
+            sb.append(";version=\"");
+            sb.append(range);
+            sb.append("\"");
+        }
+    }
+
+    private File findInClasspathDir(String file) {
+        for (File cp : classpath) {
+            if (cp.isDirectory()) {
+                File path = new File(cp, file);
+                if (path.exists()) {
+                    return path;
+                }
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java
new file mode 100644
index 0000000..7496121
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+public class BldAttr {
+	// Sigil attributes
+	
+	public static final String KIND_ATTRIBUTE = "kind";
+	
+	public static final String RESOLVE_ATTRIBUTE = "resolve";
+	public static final String RESOLVE_AUTO = "auto";
+	public static final String RESOLVE_COMPILE = "compile";
+	public static final String RESOLVE_RUNTIME = "runtime";
+	public static final String RESOLVE_IGNORE = "ignore";
+	
+	public static final String PUBLISH_ATTRIBUTE = "publish";
+	public static final String PUBTYPE_ATTRIBUTE = "type";
+	public static final String PATH_ATTRIBUTE = "path";
+	public static final Object ZONE_ATTRIBUTE = "zone";
+	
+	// Sigil options
+	
+	public static final String OPTION_ADD_IMPORTS = "addMissingImports";
+	public static final String OPTION_OMIT_IMPORTS = "omitUnusedImports";
+	
+	// OSGi attributes
+	
+	public static final String RESOLUTION_ATTRIBUTE = "resolution";
+	public static final String RESOLUTION_OPTIONAL = "optional";
+	
+	public static final String VERSION_ATTRIBUTE = "version";
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java
new file mode 100644
index 0000000..225406a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.cauldron.bld.core.util.QuoteUtil;
+
+public class BldConfig {
+	
+	// control properties
+	public static final String C_BUNDLES = "-bundles";
+	public static final String C_REPOSITORIES = "-repositories";
+
+	// string properties
+	public static final String S_ACTIVATOR = "-activator";
+	public static final String S_DEFAULTS = "-defaults";
+	public static final String S_ID = "id";
+	public static final String S_SYM_NAME = "name";
+	public static final String S_VERSION = "version";
+	public static final String[] STRING_KEYS = { S_ACTIVATOR, S_DEFAULTS, S_ID, S_SYM_NAME, S_VERSION };
+
+	// list properties
+	public static final String L_COMPOSITES = "-composites";
+	public static final String L_CONTENTS = "-contents";
+	public static final String L_DL_CONTENTS = "-downloads";
+	public static final String L_SRC_CONTENTS = "-sourcedirs";
+	public static final String L_RESOURCES = "-resources";
+	public static final String[] LIST_KEYS = {
+		L_COMPOSITES, L_CONTENTS, L_DL_CONTENTS, L_SRC_CONTENTS, L_RESOURCES };
+
+	// map properties
+	public static final String M_EXPORTS = "-exports";
+	public static final String M_IMPORTS = "-imports";
+	public static final String M_REQUIRES = "-requires";
+	public static final String M_FRAGMENT = "-fragment";
+	public static final String M_LIBS = "-libs";
+	public static final String[] MAP_KEYS = { M_EXPORTS, M_IMPORTS, M_REQUIRES, M_FRAGMENT, M_LIBS };
+	
+	// property properties
+	public static final String P_HEADER = "header";
+	public static final String P_OPTION = "option";
+	public static final String P_PACKAGE_VERSION = "package";
+	public static final String P_BUNDLE_VERSION = "bundle";
+	public static final String[] PROP_KEYS = { P_HEADER, P_OPTION, P_PACKAGE_VERSION, P_BUNDLE_VERSION };
+
+	// private constants
+	private static final String LIST_REGEX = ",\\s*";
+	private static final String MAPATTR_REGEX = ";\\s*";
+	private static final String MAPATTR_SEP = ";";
+	private static final String SUBKEY_SEP = ";";
+
+	// configuration is stored in typed maps
+	private Map<String, String> string = new TreeMap<String, String>();
+	private Map<String, List<String>> list = new TreeMap<String, List<String>>();
+	private Map<String, Map<String, Map<String, String>>> map = new TreeMap<String, Map<String,Map<String,String>>>();
+	private Map<String, BldConfig> config = new TreeMap<String, BldConfig>();
+	private Map<String, Properties> property = new TreeMap<String, Properties>();
+	
+	// default config - not modified or saved
+	private BldConfig dflt;
+	
+	private Properties unknown = new Properties();
+	private String comment = "";
+	
+	public BldConfig() {
+	}
+		
+	public BldConfig(Properties p) throws IOException {
+		merge(p);
+	}
+	
+	public void setDefault(BldConfig dflt) {
+		this.dflt = dflt;
+	}
+
+	public void setComment(String comment) {
+		this.comment = comment;
+	}
+	
+	public Properties getUnknown() {
+		return unknown;
+	}
+
+	public String getString(String id, String key) {
+		if (id != null && config.containsKey(id)) {
+			String value = config.get(id).getString(null, key);
+			if (value != null)
+				return value;
+		}
+		return string.containsKey(key) ? string.get(key) : (dflt != null ? dflt.getString(id, key) : null);
+	}
+
+	public List<String> getList(String id, String key) {
+		if (id != null && config.containsKey(id)) {
+			List<String> value = config.get(id).getList(null, key);
+			if (value != null)
+				return value;
+		}
+		return list.containsKey(key) ? list.get(key) : (dflt != null ? dflt.getList(id, key) : Collections.<String>emptyList());
+	}
+
+	public Map<String, Map<String,String>> getMap(String id, String key) {
+		if (id != null && config.containsKey(id)) {
+			Map<String, Map<String,String>> value = config.get(id).getMap(null, key);
+			if (value != null)
+				return value;
+		}
+		return map.containsKey(key) ? map.get(key)
+				: (dflt != null ? dflt.getMap(id, key) : Collections.<String, Map<String,String>>emptyMap());
+	}
+	
+	public void setString(String id, String key, String value) {
+		if (!value.equals(getString(id, key))) {
+			if (id != null) {
+    			if (!config.containsKey(id))
+    				config.put(id, new BldConfig());
+    			config.get(id).setString(null, key, value);
+			} else {
+				String dval = (dflt == null ? dflt.getString(null, key) : null);
+				if (value.equals("") && (dval == null || dval.equals(""))) {
+    		        string.remove(key);	
+    			} else {
+            		string.put(key, value);
+    			}
+			}
+		}
+	}
+
+	public void setList(String id, String key, List<String> value) {
+		if (!value.equals(getList(id, key))) {
+			if (id != null) {
+    			if (!config.containsKey(id))
+    				config.put(id, new BldConfig());
+    			config.get(id).setList(null, key, value);
+			} else if (value.isEmpty() && (dflt == null || dflt.getList(null, key).isEmpty())) {
+		        list.remove(key);	
+			} else {
+        		list.put(key, value);
+			}
+		}
+	}
+
+	public void setMap(String id, String key, Map<String, Map<String,String>> value) {
+		if (!value.equals(getMap(id, key))) {
+			if (id != null) {
+    			if (!config.containsKey(id))
+    				config.put(id, new BldConfig());
+    			config.get(id).setMap(null, key, value);
+			} else if (value.isEmpty() && (dflt == null || dflt.getMap(null, key).isEmpty())) {
+		        map.remove(key);	
+			} else {
+        		map.put(key, value);
+			}
+		}
+	}
+	
+	public Properties getProps(String id, String key) {
+    	// merge main and sub properties
+		Properties props = new Properties();
+		
+		if (dflt != null)
+    		props.putAll(dflt.getProps(id, key));
+		
+		if (property.containsKey(key))
+			props.putAll(property.get(key));
+
+		if (id != null && config.containsKey(id)) {
+			Properties p2 = config.get(id).getProps(null, key);
+			if (p2 != null)
+				props.putAll(p2);
+		}
+
+		return props;
+	}
+	
+	// only sets one property at a time
+	public void setProp(String id, String key, String k2, String v2) {
+		if (v2 == null)
+			return;
+		Properties props = getProps(id, key);
+		if (!v2.equals(props.getProperty(key))) {
+			if (id != null) {
+    			if (!config.containsKey(id))
+    				config.put(id, new BldConfig());
+    			config.get(id).setProp(null, key, k2, v2);
+			} else {
+        		if (property.containsKey(key)) {
+        			property.get(key).put(k2, v2);
+        		} else {
+            		Properties value = new Properties();
+            		value.put(k2, v2);
+            		property.put(key, value);
+        		}
+			}
+		}
+	}
+
+	/**
+	 * write config in Property file format.
+	 * This allows us to make it prettier than Properties.store().
+	 */
+	public void write(final PrintWriter out) {
+		out.println(comment);
+		
+		// Note: don't add date stamp, or file will differ each time it's saved.
+		out.println("# sigil project file, saved by plugin.\n");
+		
+		dump("", new Properties() {
+			private static final long serialVersionUID = 1L; //appease eclipse
+			@Override
+			public Object put(Object key, Object value) {
+				if (value instanceof String) {
+					out.println(key + ": " + value);
+					out.println("");
+				} else if (value instanceof List) {
+					out.println(key + ": \\");
+					for (Object k : (List<?>) value) {
+						out.println("\t" + k + ", \\");
+					}
+					out.println("");
+				}
+				else if (value instanceof Map) {
+					out.println(key + ": \\");
+					StringBuilder b = new StringBuilder();
+					for (Map.Entry<?, ?> e : ((Map<?,?>) value).entrySet()) {
+						b.append("\t");
+						b.append(e.getKey());
+						Map<?, ?> v = (Map<?, ?>) e.getValue();
+						if (!v.isEmpty()) {
+							for (Map.Entry<?, ?> e2 : v.entrySet()) {
+    							b.append(MAPATTR_SEP);
+								b.append(e2.getKey());
+								b.append("=");
+								String v2 = e2.getValue().toString();
+								if (v2.contains(",")) {
+									b.append("\"");
+									b.append(v2);
+									b.append("\"");
+								}
+								else {
+									b.append(v2);
+								}
+							}
+						}
+						b.append (", \\\n"); 
+					}
+					out.println(b.toString());
+				}
+				return null;
+			}
+		});
+		out.println("# end");
+	}
+
+	/**
+	 * dump config in pseudo Properties format.
+	 * Note: some values are not Strings (they're List<String>).
+	 */
+	private void dump(String prefix, Properties p) {
+		for (String key : string.keySet()) {
+			p.put(prefix + key, string.get(key));
+		}
+		
+		for (String key : list.keySet()) {
+			List<String> list2 = list.get(key);
+			p.put(prefix + key, list2);
+		}
+
+		for (String key : map.keySet()) {
+			Map<String, Map<String,String>> map2 = map.get(key);
+			p.put(prefix + key, map2);
+		}
+
+		for (String key : property.keySet()) {
+			Properties props = property.get(key);
+			for (Object k2 : props.keySet()) {
+				p.put(prefix + key + SUBKEY_SEP + k2, props.get(k2));
+			}
+		}
+
+		for (String key : config.keySet()) {
+			BldConfig config2 = config.get(key);
+			config2.dump(key + SUBKEY_SEP + prefix, p);
+		}
+
+		for (Object key : unknown.keySet()) {
+			String value = unknown.getProperty((String)key);
+			if (value.length() > 0)
+    			p.put(prefix + key, value);
+		}
+	}
+
+	/**
+	 * merges properties into current configuration.
+	 * @param p
+	 * @throws IOException 
+	 */
+	public void merge(Properties p) throws IOException {
+		if (p.isEmpty())
+			return;
+		
+		final List<String> strings = Arrays.asList(STRING_KEYS);
+		final List<String> lists = Arrays.asList(LIST_KEYS);
+		final List<String> maps = Arrays.asList(MAP_KEYS);
+
+		List<String> bundleKeys = new ArrayList<String>();
+		List<String> repoKeys = new ArrayList<String>();
+
+		String bundles = p.getProperty(C_BUNDLES);
+		if (bundles != null) {
+			bundleKeys.addAll(Arrays.asList(bundles.split(LIST_REGEX)));
+			list.put(C_BUNDLES, bundleKeys);
+		}
+
+		String repos = p.getProperty(C_REPOSITORIES);
+		if (repos != null) {
+			for ( String s : repos.split(LIST_REGEX) ) {
+				repoKeys.add(s.trim());
+			}
+			list.put(C_REPOSITORIES, repoKeys);
+		}
+
+		List<String> subKeys = new ArrayList<String>();
+		subKeys.addAll(Arrays.asList(PROP_KEYS));
+		subKeys.addAll(bundleKeys);
+		subKeys.addAll(repoKeys);
+
+		Map<String, Properties> sub = new TreeMap<String, Properties>();
+
+		for (Object k : p.keySet()) {
+			String key = (String) k;
+			if (key.equals(C_BUNDLES) || key.equals(C_REPOSITORIES))
+				continue;
+			
+			String value = p.getProperty(key);
+			String[] keys = key.split(SUBKEY_SEP, 2);
+
+			if (keys.length > 1) {
+				Properties p2 = sub.get(keys[0]);
+				if (p2 == null) {
+					p2 = new Properties();
+					sub.put(keys[0], p2);
+					if (!subKeys.contains(keys[0])) {
+						unknown.setProperty(keys[0] + SUBKEY_SEP, "");
+					}
+				}
+				p2.setProperty(keys[1], value);
+			} else if (strings.contains(key)) {
+				if (!string.containsKey(key))
+					string.put(key, value);
+			} else if (lists.contains(key)) {
+				if (!list.containsKey(key)) {
+					ArrayList<String> list2 = new ArrayList<String>();
+					for (String s : value.split(LIST_REGEX)) {
+						if ( s.trim().length() > 0 ) {
+							list2.add(s.trim());
+						}
+					}
+					if ( !list2.isEmpty() ) {
+						list.put(key, list2);
+					}
+				}
+			} else if (maps.contains(key)) {
+				if (!map.containsKey(key)) {
+					Map<String, Map<String,String>> map2 = new TreeMap<String, Map<String,String>>();
+
+					for (String subValue : QuoteUtil.split(value)) {
+						if (subValue.trim().length() > 0) {
+							String[] split = subValue.split(MAPATTR_REGEX);
+							Map<String,String> map3 = new TreeMap<String,String>();
+							for (int i = 1; i < split.length; ++i){
+								String[] keyVal = split[i].split(":?=", 2);
+								if (keyVal.length != 2) {
+								    throw new IOException("attribute missing '=':" + subValue);
+								}
+								map3.put(keyVal[0], keyVal[1]);
+							}
+							map2.put(split[0], map3);
+						}
+					}
+
+					map.put(key, map2);
+				}
+			} else {
+				unknown.setProperty(key, value);
+			}
+		}
+		
+		for (String subKey : sub.keySet()) {
+			Properties props = sub.get(subKey);
+			if (!props.isEmpty()) {
+				if (bundleKeys.contains(subKey)) {
+					BldConfig config2 = new BldConfig(props);
+					Properties unkProps = config2.getUnknown();
+					
+					if (config2.map.containsKey(M_IMPORTS))
+						unkProps.setProperty(M_IMPORTS, "");
+						
+					if (config2.map.containsKey(M_REQUIRES))
+						unkProps.setProperty(M_REQUIRES, "");
+						
+					for (Object unk : unkProps.keySet()) {
+						unknown.setProperty(subKey + SUBKEY_SEP + unk, "");
+					}
+					config.put(subKey, config2);
+				} else {
+					property.put(subKey, props);
+				}
+			}
+		}
+	}
+	
+	@Override
+	public String toString() {
+		return "string: " + string + " list:" + list + " map: " + map + " prop: " + property + " config:" + config;
+	}
+
+}
+
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java
new file mode 100644
index 0000000..0f8a332
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Version;
+
+import aQute.lib.osgi.Constants;
+
+public class BldConverter {
+	private static final String classpathFormat = "<classpathentry kind=\"%s\" path=\"%s\"/>";
+	private BldConfig config;
+	private Properties packageDefaults;
+	private TreeSet<String> packageWildDefaults;
+
+	public BldConverter(BldConfig config) {
+		this.config = config;
+	}
+
+	/**
+	 * converts to an ISigilBundle.
+	 * @param id
+	 * @param bundle
+	 * @return
+	 */
+	public ISigilBundle getBundle(String id, IBldBundle bundle) {
+
+		ISigilBundle sigilBundle = new SigilBundle();
+		IBundleModelElement info = new BundleModelElement();
+		sigilBundle.setBundleInfo(info);
+
+		// exports
+		// FIXME: UI doesn't understand export wildcard packages
+		for (IPackageExport export : bundle.getExports()) {
+			IPackageExport clone = (IPackageExport) export.clone();
+			clone.setParent(null);
+			info.addExport(clone);
+		}
+
+		// imports
+		for (IPackageImport import1 : bundle.getImports()) {
+			IPackageImport clone = (IPackageImport) import1.clone();
+			clone.setParent(null);
+			info.addImport(clone);
+		}
+
+		// requires
+		for (IRequiredBundle require : bundle.getRequires()) {
+			IRequiredBundle clone = (IRequiredBundle) require.clone();
+			clone.setParent(null);
+			info.addRequiredBundle(clone);
+		}
+
+		// fragment
+		IRequiredBundle fragment = bundle.getFragmentHost();
+		if (fragment != null) {
+			info.setFragmentHost(fragment);
+		}
+		
+		// contents
+		for (String pkg : bundle.getContents()) {
+			sigilBundle.addPackage(pkg);
+		}
+		
+		// downloads
+		for (String pkg : bundle.getDownloadContents()) {
+			sigilBundle.addDownloadPackage(pkg);
+		}
+		
+		// sources
+		for (String source : config.getList(null, BldConfig.L_SRC_CONTENTS) ) {
+			sigilBundle.addClasspathEntry(String.format(classpathFormat, "src", source));
+		}
+
+		// libs
+		Map<String, Map<String, String>> libs = bundle.getLibs();
+		
+		for (String path : libs.keySet()) {
+			Map<String, String> attr = libs.get(path);
+			String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
+			String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
+			
+			if (publish != null) {
+        		// FIXME: UI doesn't understand publish=name
+				BldCore.error("Can't convert -libs publish=" + publish);
+				continue;	
+			}
+			
+			if ("classpath".equals(kind)) {
+    			sigilBundle.addClasspathEntry(String.format(classpathFormat, "lib", path));
+			} else {
+				BldCore.error("Can't convert -libs kind=" + kind);
+			}
+		}
+
+		// resources
+		// FIXME: UI doesn't support -resources: path1=path2
+		Map<String, String> resources = bundle.getResources();
+		for (String resource : resources.keySet()) {
+			String fsPath = resources.get(resource);
+			if (!"".equals(fsPath)) {
+    			BldCore.error("FIXME: can't convert resource: " + resource + "=" + fsPath);
+			}
+			sigilBundle.addSourcePath(new Path(resource));
+		}
+		
+		////////////////////
+		// simple headers
+
+		info.setSymbolicName(bundle.getSymbolicName());
+
+		info.setVersion(Version.parseVersion(bundle.getVersion()));
+
+		String activator = bundle.getActivator();
+		if (activator != null)
+			info.setActivator(activator);
+		
+		Properties headers = config.getProps(id, BldConfig.P_HEADER);
+		String header;
+
+		header = headers.getProperty("CATEGORY");
+		if (header != null)
+			info.setCategory(header);
+
+		header = headers.getProperty(Constants.BUNDLE_CONTACTADDRESS);
+		if (header != null)
+			info.setContactAddress(header);
+
+		header = headers.getProperty(Constants.BUNDLE_COPYRIGHT);
+		if (header != null)
+			info.setCopyright(header);
+
+		header = headers.getProperty(Constants.BUNDLE_DESCRIPTION);
+		if (header != null)
+			info.setDescription(header);
+
+		header = headers.getProperty(Constants.BUNDLE_VENDOR);
+		if (header != null)
+			info.setVendor(header);
+
+		header = headers.getProperty(Constants.BUNDLE_NAME);
+		if (header != null)
+			info.setName(header);
+
+		header = headers.getProperty(Constants.BUNDLE_DOCURL);
+		if (header != null)
+			info.setDocURI(URI.create(header));
+
+		header = headers.getProperty(Constants.BUNDLE_LICENSE);
+		if (header != null)
+			info.setDocURI(URI.create(header));
+
+		return sigilBundle;
+	}
+
+	private VersionRange defaultVersion(VersionRange current, String defaultRange) {
+		if (current.equals(VersionRange.ANY_VERSION) ||
+			current.equals(VersionRange.parseVersionRange(defaultRange))) {
+			return null;
+		}
+		return current;
+	}
+	
+	// FIXME - copied from BldProject
+	private String getDefaultPackageVersion(String name) {
+		if (packageDefaults == null) {
+    		packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+    		packageWildDefaults = new TreeSet<String>();
+    		
+    		for (Object key : packageDefaults.keySet()) {
+    			String pkg = (String)key;
+    			if (pkg.endsWith("*")) {
+    				packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
+    			}
+    		}
+		}
+		
+		String version = packageDefaults.getProperty(name);
+		
+		if (version == null) {
+			for (String pkg : packageWildDefaults) {
+				if (name.startsWith(pkg)) {
+					version = packageDefaults.getProperty(pkg + "*");
+					// break; -- don't break, as we want the longest match
+				}
+			}
+		}
+		
+	    return version;
+	}
+	
+	
+	/**
+	 * converts from an ISigilBundle.
+	 * 
+	 * @param id
+	 * @param bundle
+	 */
+	public void setBundle(String id, ISigilBundle bundle) {
+		IBundleModelElement info = bundle.getBundleInfo();
+		String bundleVersion = config.getString(id, BldConfig.S_VERSION);
+
+		// exports
+		Map<String, Map<String, String>> exports = new TreeMap<String, Map<String,String>>();
+		for (IPackageExport export : info.getExports()) {
+			Map<String, String> map2 = new TreeMap<String, String>();
+			String version = export.getVersion().toString();
+			if (!version.equals(bundleVersion))
+				map2.put(BldAttr.VERSION_ATTRIBUTE, version);
+			exports.put(export.getPackageName(), map2);
+		}
+		
+		if (!exports.isEmpty() || !config.getMap(id, BldConfig.M_EXPORTS).isEmpty()) {
+			config.setMap(id, BldConfig.M_EXPORTS, exports);
+		}
+
+		// imports
+		Map<String, Map<String, String>> imports = new TreeMap<String, Map<String,String>>();
+
+		// FIXME: default version logic is wrong here
+		//    if the version to be saved is the same as the default version,
+		//    then we should _remove_ the version from the value being saved,
+		//    since config.getMap() does not apply default versions.
+		for (IPackageImport import1 : info.getImports()) {
+			Map<String, String> map2 = new TreeMap<String, String>();
+			String name = import1.getPackageName();
+			VersionRange versions = defaultVersion(import1.getVersions(), getDefaultPackageVersion(name));
+			
+			boolean isDependency = import1.isDependency();
+			Map<String, String> selfImport = exports.get(name);
+			
+			if (selfImport != null) {
+    			// avoid saving self-import attributes, e.g.
+    			// org.cauldron.newton.example.fractal.engine;resolve=auto;version=1.0.0
+				isDependency = true;
+				
+				if (versions != null) {
+    				String exportVersion = selfImport.get(BldAttr.VERSION_ATTRIBUTE);
+    				if (exportVersion == null)
+    					exportVersion = bundleVersion;
+    				
+    				if (exportVersion.equals(versions.toString())) {
+    					versions = null;
+    				}
+				}
+			}
+
+			if (versions != null) {
+				map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+			}
+			
+			if (import1.isOptional()) {
+				map2.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
+			}
+			
+			String resolve = BldProject.getResolve(import1, isDependency);
+			if (resolve != null)
+				map2.put(BldAttr.RESOLVE_ATTRIBUTE, resolve);
+			
+			imports.put(name, map2);
+		}
+		if (!imports.isEmpty() || !config.getMap(id, BldConfig.M_IMPORTS).isEmpty()) {
+			config.setMap(id, BldConfig.M_IMPORTS, imports);
+		}
+
+		// requires
+		Properties defaultBundles = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
+		Map<String, Map<String, String>> requires = new TreeMap<String, Map<String,String>>();
+		
+		for (IRequiredBundle require : info.getRequiredBundles()) {
+			Map<String, String> map2 = new TreeMap<String, String>();
+			String name = require.getSymbolicName();
+			VersionRange versions = defaultVersion(require.getVersions(), defaultBundles.getProperty(name));
+			if (versions != null)
+    			map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+			requires.put(name, map2);
+		}
+		if (!requires.isEmpty() || !config.getMap(id, BldConfig.M_REQUIRES).isEmpty()) {
+			config.setMap(id, BldConfig.M_REQUIRES, requires);
+		}
+
+		// fragment
+		Map<String, Map<String, String>> fragments = new TreeMap<String, Map<String,String>>();
+		IRequiredBundle fragment = info.getFragmentHost();
+		if (fragment != null) {
+			Map<String, String> map2 = new TreeMap<String, String>();
+			String name = fragment.getSymbolicName();
+			VersionRange versions = defaultVersion(fragment.getVersions(), defaultBundles.getProperty(name));
+			if (versions != null)
+    			map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+			fragments.put(name, map2);
+		}
+		if (!fragments.isEmpty() || !config.getMap(id, BldConfig.M_FRAGMENT).isEmpty()) {
+			config.setMap(id, BldConfig.M_FRAGMENT, fragments);
+		}
+	
+		// contents
+		List<String> contents = new ArrayList<String>();
+		for (String pkg : bundle.getPackages()) {
+			contents.add(pkg);
+		}
+		if (!contents.isEmpty() || !config.getList(id, BldConfig.L_CONTENTS).isEmpty()) {
+			config.setList(id, BldConfig.L_CONTENTS, contents);
+		}
+		
+		// dl contents
+		List<String> dlcontents = new ArrayList<String>();
+		for (String pkg : bundle.getDownloadPackages()) {
+			dlcontents.add(pkg);
+		}
+		if (!dlcontents.isEmpty() || !config.getList(id, BldConfig.L_DL_CONTENTS).isEmpty()) {
+			config.setList(id, BldConfig.L_DL_CONTENTS, dlcontents);
+		}
+
+		// libs
+		Map<String, Map<String, String>> libs = new TreeMap<String, Map<String,String>>();
+		List<String> sources = new ArrayList<String>();
+
+		// classpathEntries map to -libs or -sources
+		for (String entry : bundle.getClasspathEntrys()) {
+			// <classpathentry kind="lib" path="lib/dependee.jar"/>
+			// <classpathentry kind="src" path="src"/>
+			final String regex = ".* kind=\"([^\"]+)\" path=\"([^\"]+)\".*";
+			Pattern pattern = Pattern.compile(regex);
+			Matcher matcher = pattern.matcher(entry);
+			if (matcher.matches()) {
+				String kind = matcher.group(1);
+				String path = matcher.group(2);
+				if (kind.equals("lib")) {
+    				Map<String, String> map2 = new TreeMap<String, String>();
+    				map2.put(BldAttr.KIND_ATTRIBUTE, "classpath");
+    				libs.put(path, map2);
+				} else if (kind.equals("src")) {
+					sources.add(path);
+				} else {
+    				BldCore.error("unknown classpathentry kind=" + kind);
+				}
+			} else {
+				BldCore.error("can't match classpathEntry in: " + entry);
+			}
+		}
+
+		if (!libs.isEmpty() || !config.getMap(id, BldConfig.M_LIBS).isEmpty()) {
+			config.setMap(id, BldConfig.M_LIBS, libs);
+		}
+
+		if (!sources.isEmpty() || !config.getList(id, BldConfig.L_SRC_CONTENTS).isEmpty()) {
+			config.setList(id, BldConfig.L_SRC_CONTENTS, sources);
+		}
+
+		// composites
+		ArrayList<String> composites = new ArrayList<String>();
+		for (ISCAComposite composite : bundle.getComposites()) {
+			String path = composite.getLocation().toString();
+			// TODO relativize
+			composites.add(path);
+		}
+		
+		if (!composites.isEmpty() || !config.getList(id, BldConfig.L_COMPOSITES).isEmpty()) {
+			Collections.sort(composites);
+			config.setList(id, BldConfig.L_COMPOSITES, composites);
+		}
+		
+		// resources
+		ArrayList<String> resources = new ArrayList<String>();
+		for (IPath ipath : bundle.getSourcePaths()) {
+			resources.add(ipath.toString());
+		}
+		
+		if (!resources.isEmpty() || !config.getList(id, BldConfig.L_RESOURCES).isEmpty()) {
+    		Collections.sort(resources);
+			config.setList(id, BldConfig.L_RESOURCES, resources);
+		}
+		
+		if (info.getSourceLocation() != null) {
+			BldCore.error("SourceLocation conversion not yet implemented.");
+		}
+
+		if (!info.getLibraryImports().isEmpty()) {
+			BldCore.error("LibraryImports conversion not yet implemented.");
+		}
+
+		////////////////////
+		// simple headers
+
+		List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
+		String idBsn = id != null ? id : ids.get(0);
+		String oldBsn = config.getString(id, BldConfig.S_SYM_NAME);
+		String bsn = info.getSymbolicName();
+		
+		if (!bsn.equals(idBsn) || oldBsn != null)
+			config.setString(id, BldConfig.S_SYM_NAME, bsn);
+
+		String version = info.getVersion().toString();
+		if (version != null)
+			config.setString(id, BldConfig.S_VERSION, version);
+
+		String activator = info.getActivator();
+		if (activator != null)
+    		config.setString(id, BldConfig.S_ACTIVATOR, activator);
+		
+		Properties headers = config.getProps(null, BldConfig.P_HEADER);
+		
+		setHeader(headers, id, "CATEGORY", info.getCategory());
+		setHeader(headers, id, Constants.BUNDLE_CONTACTADDRESS, info.getContactAddress());
+		setHeader(headers, id, Constants.BUNDLE_COPYRIGHT, info.getCopyright());
+		setHeader(headers, id, Constants.BUNDLE_DESCRIPTION, info.getDescription());
+		setHeader(headers, id, Constants.BUNDLE_VENDOR, info.getVendor());
+		setHeader(headers, id, Constants.BUNDLE_NAME, info.getName());
+
+		if (info.getDocURI() != null)
+			config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_DOCURL, info.getDocURI().toString());
+
+		if (info.getLicenseURI() != null)
+			config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_LICENSE, info.getLicenseURI().toString());
+	}
+	
+	private void setHeader(Properties headers, String id, String key, String value) {
+		if (value == null)
+			value = "";
+		if (!value.equals(headers.getProperty(key, "")))
+    		config.setProp(id, BldConfig.P_HEADER, key, value);
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java
new file mode 100644
index 0000000..a02adccc
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class BldFactory {
+	private static Map<URI, BldProject> projects = new HashMap<URI, BldProject>();
+	
+	public static IBldProject getProject(URI uri) throws IOException {
+		return getProject(uri, false);
+	}
+	
+	public static IBldProject getProject(URI uri, boolean ignoreCache) throws IOException {
+		return load(uri, ignoreCache);
+	}
+	
+	public static IRepositoryConfig getConfig(URI uri) throws IOException {
+		return load(uri, false);
+	}
+	
+	/**
+	 * creates a new project file, initialised with defaults.
+	 * @param uri where the file will be saved - used to resolve relative paths.
+	 * @param defaults relative path to defaults file - default ../sigil.properties.
+	 * @return
+	 * @throws IOException
+	 */
+	public static IBldProject newProject(URI uri, String defaults) throws IOException {
+		BldProject project = new BldProject(uri);
+		Properties p = new Properties();
+		if (defaults != null)
+    		p.setProperty(BldConfig.S_DEFAULTS, defaults);
+		project.loadDefaults(p);
+		return project;
+	}
+	
+	private static BldProject load(URI uri, boolean ignoreCache) throws IOException {
+		BldProject p = null; 
+		if (!ignoreCache) {
+			p = projects.get(uri);
+		}
+		
+		if (p == null) {
+			p = new BldProject(uri);
+			p.load();
+			projects.put(uri, p);
+    			
+    		if (Boolean.getBoolean("org.cauldron.bld.config.test")) {
+    			File path = new File(uri.getPath() + ".tmp");
+    			System.out.println("XXX: config.test writing: " + path);
+    			p.saveAs(path);
+    		}
+		}
+		return p;
+	}
+	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java
new file mode 100644
index 0000000..585a029
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java
@@ -0,0 +1,823 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeSet;
+
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageExport;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport.OSGiImport;
+import org.osgi.framework.Version;
+
+public class BldProject implements IBldProject, IRepositoryConfig {
+    private static final String OVERRIDE_PREFIX = "sigil.";
+    private static final int MAX_HEADER = 10240;
+    // cache to avoid loading the same default config for each project
+    private static Map<URL, BldConfig> defaultsCache = new HashMap<URL, BldConfig>();
+    private static Properties overrides;
+
+    private List<String> sourcePkgs;
+    private BldConfig config;
+    private BldConverter convert;
+    private BundleModelElement requirements;
+    private File baseDir;
+    private URI loc;
+    private Properties packageDefaults;
+    private TreeSet<String> packageWildDefaults;
+    private long lastModified;
+
+    /* package */BldProject(URI relLoc) {
+        config = new BldConfig();
+        convert = new BldConverter(config);
+        loc = new File(".").toURI().resolve(relLoc).normalize();
+        File f = new File(loc);
+        lastModified = f.lastModified();
+        baseDir = f.getParentFile();
+    }
+
+    /* package */void load() throws IOException {
+        // allow System property overrides, e.g.
+        // ANT_OPTS='-Dsigil.option\;addMissingImports=false' ant
+        config.merge(getOverrides());
+
+        InputStream in = null;
+        try {
+        	in = loc.toURL().openStream();
+	        BufferedInputStream bis = new BufferedInputStream(in);
+	        bis.mark(MAX_HEADER);
+	        readHeader(bis);
+	        bis.reset();
+	
+	        Properties p = new Properties();
+	        p.load(bis);
+	        config.merge(p);
+	
+	        Properties unknown = config.getUnknown();
+	        if (!unknown.isEmpty())
+	            System.err.println("WARN: unknown keys " + unknown.keySet() + " in " + loc);
+	
+	        loadDefaults(p);
+	        requirements = parseRequirements();
+        }
+        finally {
+        	if ( in != null ) {
+        		in.close();
+        	}
+        }
+    }
+
+    /* package */void loadDefaults(Properties p) throws IOException {
+        BldConfig c = loadDefaults(p, baseDir, null);
+        config.setDefault(c);
+
+        Properties options = config.getProps(null, BldConfig.P_OPTION);
+
+        if (!options.containsKey(BldAttr.OPTION_ADD_IMPORTS))
+            c.setProp(null, BldConfig.P_OPTION, BldAttr.OPTION_ADD_IMPORTS, "true");
+
+        // default omitUnusedImports option depends on number of bundles...
+        // we set it here to avoid it being written by save(),
+        // but as this may alter cached defaults, once set we have to reset it
+        // for each project.
+
+        boolean omitSet = options.containsKey("__omit_set__");
+        boolean multiple = getBundleIds().size() > 1;
+
+        if (multiple || omitSet) {
+            if (!options.containsKey(BldAttr.OPTION_OMIT_IMPORTS) || omitSet) {
+                c.setProp(null, BldConfig.P_OPTION, BldAttr.OPTION_OMIT_IMPORTS, multiple + "");
+                c.setProp(null, BldConfig.P_OPTION, "__omit_set__", "true");
+            }
+        }
+    }
+
+    private synchronized BldConfig loadDefaults(Properties props, File base, BldConfig dflt)
+            throws IOException {
+        boolean cached = false;
+        String defaults = props.getProperty(BldConfig.S_DEFAULTS, "-"
+                + IBldProject.PROJECT_DEFAULTS);
+
+        if (base != null && defaults.length() > 0) {
+            boolean ignore = defaults.startsWith("-");
+
+            if (ignore)
+                defaults = defaults.substring(1);
+
+            try {
+                File file = new File(base, defaults).getCanonicalFile();
+                URL url = file.toURL();
+
+                if (dflt == null) {
+                    dflt = defaultsCache.get(url);
+                    if (dflt != null)
+                        return dflt;
+
+                    dflt = new BldConfig();
+                    defaultsCache.put(url, dflt);
+                    cached = true;
+                }
+
+                Properties p = new Properties();
+                p.load(url.openStream());
+                dflt.merge(p);
+
+                ignore = false;
+                loadDefaults(p, file.getParentFile(), dflt);
+            } catch (IOException e) {
+                if (!ignore)
+                    throw e;
+            }
+        }
+
+        if (dflt == null)
+            return new BldConfig();
+
+        if (cached) {
+            Properties unknown = dflt.getUnknown();
+            if (!unknown.isEmpty())
+                System.err.println("WARN: unknown keys " + unknown.keySet() + " in defaults for "
+                        + loc);
+        }
+
+        return dflt;
+    }
+
+    private static Properties getOverrides() {
+        if (overrides == null) {
+            overrides = new Properties();
+            Properties sysProps = System.getProperties();
+
+            for (Object okey : sysProps.keySet()) {
+                String key = (String) okey;
+                if (key.startsWith(OVERRIDE_PREFIX)) {
+                    overrides.setProperty(key.substring(OVERRIDE_PREFIX.length()), sysProps
+                            .getProperty(key));
+                }
+            }
+        }
+
+        return overrides;
+    }
+
+    private void readHeader(InputStream in) throws IOException {
+        BufferedReader r = new BufferedReader(new InputStreamReader(in));
+        StringBuffer header = new StringBuffer();
+        String line;
+        while ((line = r.readLine()) != null) {
+            if (line.startsWith("#")) {
+                header.append(line);
+                header.append("\n");
+            } else {
+                config.setComment(header.toString());
+                break;
+            }
+        }
+    }
+
+    public File resolve(String path) {
+        File file = new File(path);
+        if (!file.isAbsolute()) {
+            // can't use loc.resolve(value), as value may not be valid URI.
+            file = new File(baseDir, path);
+        }
+        return file;
+    }
+
+    public String getVersion() {
+        String version = config.getString(null, BldConfig.S_VERSION);
+        return version == null ? "0" : version;
+    }
+
+    public IBundleModelElement getDependencies() {
+        IBundleModelElement dependencies = new BundleModelElement();
+
+        for (IModelElement element : getRequirements().children()) {
+            if (element instanceof IPackageImport) {
+                IPackageImport import1 = (IPackageImport) element;
+                if (!import1.isDependency())
+                    continue;
+
+                IPackageImport pi = (IPackageImport) (element.clone());
+                pi.setParent(null);
+                dependencies.addImport(pi);
+            } else {
+                IRequiredBundle rb = (IRequiredBundle) (element.clone());
+                rb.setParent(null);
+                dependencies.addRequiredBundle(rb);
+            }
+        }
+
+        boolean containsComposite = false;
+
+        for (IBldBundle bundle : getBundles()) {
+            if (!bundle.getComposites().isEmpty()) {
+                containsComposite = true;
+                break;
+            }
+        }
+
+        // add dependency on component activator
+        if (containsComposite) {
+            PackageImport pi = new PackageImport();
+            pi.setPackageName(BundleBuilder.COMPONENT_ACTIVATOR_PKG);
+            pi.setOSGiImport(OSGiImport.NEVER);
+            dependencies.addImport(pi);
+        }
+
+        return dependencies;
+    }
+
+    private IBundleModelElement getRequirements() {
+        return requirements;
+    }
+
+    /*
+     * private boolean globMatch(String pkg, Set<String> set) { // exact match
+     * if (set.contains(pkg)) return true;
+     * 
+     * // org.foo.bar matches org.foo. for (String glob : set) { if
+     * (glob.matches(pkg)) { return true; } }
+     * 
+     * return false; }
+     */
+
+    /**
+     * set internal OSGiImport and isDependency flags, based on external
+     * resolution= attribute.
+     * 
+     * OSGiImport: AUTO ALWAYS NEVER dependency: default - compile !dependency:
+     * auto runtime ignore
+     * 
+     */
+    private void setResolve(IPackageImport pi, String resolve) throws IOException {
+        if (pi.isOptional())
+            pi.setDependency(false);
+
+        if (BldAttr.RESOLVE_COMPILE.equals(resolve)) {
+            if (pi.isOptional())
+                pi.setDependency(true);
+            else
+                pi.setOSGiImport(OSGiImport.NEVER);
+        } else if (BldAttr.RESOLVE_RUNTIME.equals(resolve)) {
+            pi.setDependency(false);
+            pi.setOSGiImport(OSGiImport.ALWAYS);
+        } else if (BldAttr.RESOLVE_AUTO.equals(resolve)) {
+            pi.setDependency(false);
+        } else if (BldAttr.RESOLVE_IGNORE.equals(resolve)) {
+            pi.setDependency(false);
+            pi.setOSGiImport(OSGiImport.NEVER);
+        } else if (resolve != null) {
+            throw new IOException("Bad attribute value: " + BldAttr.RESOLVE_ATTRIBUTE + "="
+                    + resolve);
+        }
+    }
+
+    /**
+     * get external resolve= attribute from internal PackageImport flags. This
+     * is called from BldConverter.setBundle().
+     */
+    public static String getResolve(IPackageImport pi, boolean isDependency) {
+        OSGiImport osgiImport = pi.getOSGiImport();
+        String resolve = null;
+
+        if (isDependency) {
+            if (osgiImport.equals(OSGiImport.NEVER) || pi.isOptional())
+                resolve = BldAttr.RESOLVE_COMPILE;
+        } else {
+            switch (osgiImport) {
+            case ALWAYS:
+                resolve = BldAttr.RESOLVE_RUNTIME;
+                break;
+            case AUTO:
+                resolve = BldAttr.RESOLVE_AUTO;
+                break;
+            case NEVER:
+                resolve = BldAttr.RESOLVE_IGNORE;
+                break;
+            }
+        }
+        return resolve;
+    }
+
+    public String getDefaultPackageVersion(String name) {
+        if (packageDefaults == null) {
+            packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+            packageWildDefaults = new TreeSet<String>();
+
+            for (Object key : packageDefaults.keySet()) {
+                String pkg = (String) key;
+                if (pkg.endsWith("*")) {
+                    packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
+                }
+            }
+        }
+
+        String version = packageDefaults.getProperty(name);
+
+        if (version == null) {
+            for (String pkg : packageWildDefaults) {
+                if (name.startsWith(pkg)) {
+                    version = packageDefaults.getProperty(pkg + "*");
+                    // break; -- don't break, as we want the longest match
+                }
+            }
+        }
+
+        return version;
+    }
+
+    private synchronized BundleModelElement parseRequirements() throws IOException {
+        BundleModelElement reqs = new BundleModelElement();
+
+        List<String> sourceContents = getSourcePkgs();
+        HashSet<String> exports = new HashSet<String>();
+
+        for (IBldBundle bundle : getBundles()) {
+            for (IPackageExport export : bundle.getExports()) {
+                exports.add(export.getPackageName());
+            }
+        }
+
+        Map<String, Map<String, String>> imports = config.getMap(null, BldConfig.M_IMPORTS);
+
+        for (String name : imports.keySet()) {
+            Map<String, String> attr = imports.get(name);
+
+            String resolve = attr.get(BldAttr.RESOLVE_ATTRIBUTE);
+            String resolution = attr.get(BldAttr.RESOLUTION_ATTRIBUTE);
+            String versions = attr.containsKey(BldAttr.VERSION_ATTRIBUTE) ? attr
+                    .get(BldAttr.VERSION_ATTRIBUTE) : getDefaultPackageVersion(name);
+
+            PackageImport pi = new PackageImport();
+            pi.setPackageName(name);
+
+            // avoid dependency on self-exports
+            // XXX: BldConverter.setBundle contains similar logic
+            if (exports.contains(name)
+                    && (sourceContents.contains(name) || sourceContents.isEmpty())) {
+                pi.setDependency(false);
+                if (versions == null)
+                    versions = getVersion();
+            }
+
+            if (!checkVersionRange(versions)) {
+                throw new IOException("Failed to parse version range for " + resolve
+                        + " missing \"'s around version range?");
+            }
+
+            pi.setVersions(VersionRange.parseVersionRange(versions));
+
+            if (BldAttr.RESOLUTION_OPTIONAL.equals(resolution)) {
+                pi.setOptional(true);
+            } else if (resolution != null) {
+                throw new IOException("Bad attribute value: " + BldAttr.RESOLUTION_ATTRIBUTE + "="
+                        + resolution);
+            }
+
+            setResolve(pi, resolve);
+
+            reqs.addImport(pi);
+        }
+
+        Map<String, Map<String, String>> requires = config.getMap(null, BldConfig.M_REQUIRES);
+        Properties bundleDefaults = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
+
+        if (requires != null) {
+            for (String name : requires.keySet()) {
+                Map<String, String> attr = requires.get(name);
+                String versions = attr.containsKey(BldAttr.VERSION_ATTRIBUTE) ? attr
+                        .get(BldAttr.VERSION_ATTRIBUTE) : bundleDefaults.getProperty(name);
+
+                RequiredBundle rb = new RequiredBundle();
+                rb.setSymbolicName(name);
+                rb.setVersions(VersionRange.parseVersionRange(versions));
+
+                reqs.addRequiredBundle(rb);
+            }
+        }
+
+        for (IBldBundle bundle : getBundles()) {
+            IRequiredBundle fh = bundle.getFragmentHost();
+            if (fh != null)
+                reqs.addRequiredBundle(fh);
+        }
+
+        return reqs;
+    }
+
+    private boolean checkVersionRange(String versions) {
+        if (versions == null || versions.length() == 0) {
+            return true;
+        } else {
+            switch (versions.charAt(0)) {
+            case '(':
+            case '[':
+                switch (versions.charAt(versions.length() - 1)) {
+                case ')':
+                case ']':
+                    return true;
+                default:
+                    return false;
+                }
+            default:
+                return true;
+            }
+        }
+    }
+
+    public List<String> getBundleIds() {
+        List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
+        if (ids == null)
+            return Collections.emptyList();
+        return ids;
+    }
+
+    public List<IBldBundle> getBundles() {
+        ArrayList<IBldBundle> list = new ArrayList<IBldBundle>();
+
+        for (String id : getBundleIds()) {
+            list.add(new BldBundle(id));
+        }
+
+        return list;
+    }
+
+    // Implement IBldConfig: getRepositoryConfig
+
+    public Map<String, Properties> getRepositoryConfig() {
+        HashMap<String, Properties> map = new HashMap<String, Properties>();
+
+        final Map<String, String> env = System.getenv();
+        final Properties props = new Properties();
+        try {
+            // supports ${.} and ${..} expansions
+            props.setProperty(".", resolve(".").getCanonicalPath());
+            props.setProperty("..", resolve("..").getCanonicalPath());
+        } catch (IOException e) {
+        }
+
+        for (String name : config.getList(null, BldConfig.C_REPOSITORIES)) {
+            Properties repo = config.getProps(null, name);
+
+            for (Object k : repo.keySet()) {
+                String key = (String) k;
+                String value = repo.getProperty(key);
+
+                String expand = BldUtil.expand(value, new Properties() {
+                    public String getProperty(String name) {
+                        return props.getProperty(name, env.get(name));
+                    }
+                });
+
+                if (!value.equals(expand)) {
+                    value = expand;
+                    repo.setProperty(key, value);
+                }
+
+                // backwards compatible support before ${.} and ${..} was added
+                if (value.startsWith("./") || value.startsWith("../")) {
+                    try {
+                        // need canonical path, to normalise
+                        value = resolve(value).getCanonicalPath();
+                    } catch (IOException e) {
+                    }
+                    repo.setProperty(key, value);
+                }
+            }
+
+            map.put(name, repo);
+        }
+        return map;
+    }
+
+    public Properties getOptions() {
+        return config.getProps(null, BldConfig.P_OPTION);
+    }
+
+    public Properties getDefaultPackageVersions() {
+        return config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+    }
+
+    public ISigilBundle getDefaultBundle() {
+        List<String> bundles = getBundleIds();
+        if (bundles.isEmpty())
+            return null;
+
+        String id = bundles.get(0);
+        return getSigilBundle(id);
+    }
+
+    public ISigilBundle getSigilBundle(String id) {
+        BldBundle bundle = new BldBundle(id);
+        return convert.getBundle(id, bundle);
+    }
+
+    public void setDefaultBundle(ISigilBundle bundle) {
+        setSigilBundle(null, bundle);
+    }
+
+    public void setSigilBundle(String id, ISigilBundle bundle) {
+        List<String> ids = getBundleIds();
+
+        if (ids.isEmpty()) {
+            ArrayList<String> list = new ArrayList<String>();
+            list.add(id == null ? bundle.getBundleInfo().getSymbolicName() : id);
+            config.setList(null, BldConfig.C_BUNDLES, list);
+        } else if (id == null) {
+            id = ids.get(0);
+        } else if (!ids.contains(id)) {
+            List<String> list = config.getList(null, BldConfig.C_BUNDLES);
+            list.add(id);
+            config.setList(null, BldConfig.C_BUNDLES, list);
+        }
+
+        if (ids.size() == 1)
+            id = null; // don't prefix default bundle with id
+
+        convert.setBundle(id, bundle);
+    }
+
+    public void save() throws IOException {
+        saveAs(new File(loc));
+    }
+
+    public void saveAs(File path) throws IOException {
+        File part = new File(path.getPath() + ".part");
+        saveTo(new FileOutputStream((part)));
+
+        path.delete();
+        if (!part.renameTo(path))
+            throw new IOException("failed to rename " + part + " to " + path);
+    }
+
+    public void saveTo(OutputStream out) {
+        PrintWriter writer = new PrintWriter(new OutputStreamWriter(out));
+        config.write(writer);
+        writer.close();
+    }
+
+    public List<String> getSourceDirs() {
+        List<String> list = config.getList(null, BldConfig.L_SRC_CONTENTS);
+        return list != null ? list : Collections.<String> emptyList();
+    }
+
+    public List<String> getSourcePkgs() {
+        if (sourcePkgs == null) {
+            sourcePkgs = new ArrayList<String>();
+            for (String src : getSourceDirs()) {
+                File dir = resolve(src);
+                if (!dir.isDirectory()) {
+                    System.err.println("WARN: sourcedir does not exist: " + dir);
+                    continue;
+                    // throw new RuntimeException("sourcedir: " + dir +
+                    // " : is not a directory.");
+                }
+                findSrcPkgs(dir, null, sourcePkgs);
+            }
+        }
+
+        return sourcePkgs;
+    }
+
+    private void findSrcPkgs(File dir, String pkg, List<String> result) {
+        ArrayList<File> dirs = new ArrayList<File>();
+        boolean found = false;
+
+        for (String name : dir.list()) {
+            if (name.endsWith(".java")) {
+                found = true;
+            } else if (!name.equals(".svn")) {
+                File d = new File(dir, name);
+                if (d.isDirectory())
+                    dirs.add(d);
+            }
+        }
+
+        if (pkg == null) {
+            pkg = "";
+        } else if (pkg.equals("")) {
+            pkg = dir.getName();
+        } else {
+            pkg = pkg + "." + dir.getName();
+        }
+
+        if (found)
+            result.add(pkg);
+
+        for (File d : dirs)
+            findSrcPkgs(d, pkg, result);
+    }
+
+    /**
+     * BldBundle
+     * 
+     */
+    class BldBundle implements IBldBundle {
+        private String id;
+
+        public BldBundle(String id) {
+            this.id = id;
+        }
+
+        public File resolve(String path) {
+            return BldProject.this.resolve(path);
+        }
+
+        private String getString(String key) {
+            return config.getString(id, key);
+        }
+
+        private List<String> getList(String key) {
+            List<String> list = config.getList(id, key);
+            return list != null ? list : Collections.<String> emptyList();
+        }
+
+        private Map<String, Map<String, String>> getMap(String key) {
+            Map<String, Map<String, String>> map = config.getMap(id, key);
+            return map != null ? map : Collections.<String, Map<String, String>> emptyMap();
+        }
+
+        public String getActivator() {
+            return getString(BldConfig.S_ACTIVATOR);
+        }
+
+        public String getId() {
+            String name = getString("id");
+            return name != null ? name : id;
+        }
+
+        public String getVersion() {
+            String ver = getString(BldConfig.S_VERSION);
+            if (ver == null) {
+                ver = BldProject.this.getVersion();
+            }
+            return ver;
+        }
+
+        public String getSymbolicName() {
+            String name = getString(BldConfig.S_SYM_NAME);
+            return name != null ? name : getId();
+        }
+
+        public List<IPackageExport> getExports() {
+            ArrayList<IPackageExport> list = new ArrayList<IPackageExport>();
+            Map<String, Map<String, String>> exports = getMap(BldConfig.M_EXPORTS);
+
+            if (exports != null) {
+                for (String name : exports.keySet()) {
+                    Map<String, String> attrs = exports.get(name);
+                    PackageExport pkgExport = new PackageExport();
+                    pkgExport.setPackageName(name);
+
+                    String version = attrs.get(BldAttr.VERSION_ATTRIBUTE);
+                    // only default export version from local packages
+                    if (version == null
+                            && (getSourcePkgs().isEmpty() || getSourcePkgs().contains(name))) {
+                        version = getVersion();
+                    }
+
+                    if (version != null)
+                        pkgExport.setVersion(new Version(version));
+
+                    list.add(pkgExport);
+                }
+            }
+
+            return list;
+        }
+
+        public List<IPackageImport> getImports() {
+            ArrayList<IPackageImport> list = new ArrayList<IPackageImport>();
+
+            for (IPackageImport import1 : getRequirements().childrenOfType(IPackageImport.class)) {
+                list.add(import1);
+            }
+
+            return list;
+        }
+
+        public List<IRequiredBundle> getRequires() {
+            ArrayList<IRequiredBundle> list = new ArrayList<IRequiredBundle>();
+            list.addAll(Arrays.asList(getRequirements().childrenOfType(IRequiredBundle.class)));
+
+            for (IBldBundle bundle : getBundles()) {
+                IRequiredBundle fh = bundle.getFragmentHost();
+                if (fh != null)
+                    list.remove(fh);
+            }
+
+            return list;
+        }
+
+        public IRequiredBundle getFragmentHost() {
+            IRequiredBundle fragment = null;
+            Map<String, Map<String, String>> fragments = getMap(BldConfig.M_FRAGMENT);
+            if (fragments != null) {
+                for (String name : fragments.keySet()) {
+                    Map<String, String> attr = fragments.get(name);
+                    String versions = attr.isEmpty() ? null : attr.get(BldAttr.VERSION_ATTRIBUTE);
+                    fragment = new RequiredBundle();
+                    fragment.setSymbolicName(name);
+                    fragment.setVersions(VersionRange.parseVersionRange(versions));
+                    break;
+                }
+            }
+
+            return fragment;
+        }
+
+        public Map<String, Map<String, String>> getLibs() {
+            Map<String, Map<String, String>> libs = getMap(BldConfig.M_LIBS);
+            return (libs != null) ? libs : Collections.<String, Map<String, String>> emptyMap();
+        }
+
+        public List<String> getContents() {
+            return getList(BldConfig.L_CONTENTS);
+        }
+
+        public List<String> getDownloadContents() {
+            return getList(BldConfig.L_DL_CONTENTS);
+        }
+
+        public List<String> getComposites() {
+            ArrayList<String> list = new ArrayList<String>();
+            for (String composite : getList(BldConfig.L_COMPOSITES)) {
+                list.add(composite);
+            }
+
+            return list;
+        }
+
+        public Map<String, String> getResources() {
+            HashMap<String, String> map = new HashMap<String, String>();
+            List<String> resources = getList(BldConfig.L_RESOURCES);
+
+            if (resources != null) {
+                for (String resource : resources) {
+                    String[] paths = resource.split("=", 2);
+                    String fsPath = (paths.length > 1 ? paths[1] : "");
+                    map.put(paths[0], fsPath);
+                }
+            }
+            return map;
+        }
+
+        public Properties getHeaders() {
+            Properties headers = config.getProps(id, BldConfig.P_HEADER);
+            return headers;
+        }
+
+    }
+
+    public long getLastModified() {
+        return lastModified;
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java
new file mode 100644
index 0000000..ad85627
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// taken from Newton Launcher
+
+public class BldUtil {
+    /**
+     * expands property references embedded in strings. Each occurrence of ${name} is replaced with the value of
+     * p.getProperty("name"); If the property is not set, then the original reference, is returned as follows "?<name>".
+     * 
+     * Strings to be expanded should not contain $ or }, except to indicate expansions.
+     * 
+     * Value is expanded recursively and so can contain further ${name} references. Also supports shell-expansions
+     * ${name:-value}, ${name:=value} and ${name:+value}.
+     * 
+     * <pre>
+     *      ${parameter}
+     *      The value of parameter is substituted.
+     *      ${parameter:-word}
+     *      Use  Default  Values.  If parameter is null, the expansion of word
+     *      is substituted.  Otherwise, the value of  parameter is substituted.
+     *      ${parameter:=word}
+     *      Assign  Default  Values.   If  parameter is null, the expansion of
+     *      word is assigned to parameter.  The value of parameter  is  then
+     *      substituted.
+     *      ${parameter:+word}
+     *      Use Alternate Value.  If parameter is null, nothing  is
+     *      substituted, otherwise the expansion of word is substituted.
+     *      ${parameter:?word}
+     *      Raise Error.  If parameter is null, a RuntimeException is thown,
+     *      with word as the message.
+     * </pre>
+     */
+    public static String expand(String s, Properties p) {
+        // regex to match property references e.g. ${name}
+        // TODO this is very simplistic, so strings to be expanded should not
+        // contain $ or }, except where substitution is expected.
+	// Update: propRef regex now allows substitutions to contain $,
+	// e.g. where a Windows ${user.name} is $Admin or similar.
+        final Pattern propRef = Pattern.compile("\\$\\{(((\\$[^\\{\\}])|[^\\$\\}])+\\$?)\\}");
+        final Pattern backslash = Pattern.compile("\\\\");
+        final Pattern dollar = Pattern.compile("\\$");
+
+        if (s == null) {
+            return null;
+        }
+
+        if (s.indexOf("${") == -1) { // shortcut if no expansions
+            return s;
+        }
+
+        for (int i = 0; i < 20; i++) { // avoids self-referencing expansions
+            // System.out.println("XXX expand[" + i + "] = [" + s + "]");
+            Matcher matcher = propRef.matcher(s);
+
+            if (!matcher.find()) {
+                // replace unmatched items
+                s = s.replaceAll("\\Q??[\\E", "\\${");
+                s = s.replaceAll("\\Q??]\\E", "}");
+                // debug("expanded: " + s);
+                if (s.indexOf("${") != -1) {
+                    throw new RuntimeException("Can't expand: " + s);
+                }
+                return s;
+            }
+
+            String key = matcher.group(1);
+            String[] keydef = key.split(":[=+-?@]", 2);
+            String replace;
+
+            if (keydef.length != 2) {
+                replace = key.length() == 0 ? null : p.getProperty(key);
+            }
+            else {
+                replace = keydef[0].length() == 0 ? null : p.getProperty(keydef[0]);
+
+                if (replace != null && (replace.length() == 0 || replace.indexOf("${") != -1)) {
+                    // don't want unexpanded replacement, as it may stop ${...:-default}
+                    replace = null;
+                }
+
+                if (key.indexOf(":+") != -1) {
+                    replace = ((replace == null) ? "" : keydef[1]);
+                }
+                else if (replace == null) {
+                    replace = keydef[1];
+
+                    if (key.indexOf(":?") != -1) {
+                        String msg = "${" + keydef[0] + ":?" + keydef[1] + "} property not set";
+                        throw new RuntimeException(msg);
+                    }
+
+                    if (key.indexOf(":=") != -1) {
+                        p.setProperty(keydef[0], keydef[1]);
+                    }
+                }
+            }
+
+            if (replace == null) {
+                // TODO: this is a hack to avoid looping on unmatched references
+                // should really leave unchanged and process rest of string.
+                // We use "]" as delimiter to avoid non-matched "}"
+                // terminating potential _propRef match
+                replace = "??[" + key + "??]";
+            }
+
+            // Excerpt from replaceAll() javadoc:
+            //
+            // Note that backslashes (\) and dollar signs ($) in the replacement
+            // string may cause the results to be different than if it were
+            // being
+            // treated as a literal replacement string. Dollar signs may be
+            // treated
+            // as references to captured subsequences, and backslashes are used
+            // to
+            // escape literal characters in the replacement string.
+            // escape any \ or $ in replacement string
+            replace = backslash.matcher(replace).replaceAll("\\\\\\\\");
+            replace = dollar.matcher(replace).replaceAll("\\\\\\$");
+
+            s = s.replaceAll("\\Q${" + key + "}\\E", replace);
+        }
+
+        throw new RuntimeException("expand: loop expanding: " + s);
+    }
+
+    public static String expand(String s) {
+        final Map<String, String> env = System.getenv();
+
+        return expand(s, new Properties() {
+            public String getProperty(String name) {
+                return System.getProperty(name, env.get(name));
+            }
+        });
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java
new file mode 100644
index 0000000..b273b7b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IBldProject {
+	
+	static final String PROJECT_FILE = "sigil.properties";
+	static final String PROJECT_DEFAULTS = "../sigil-defaults.properties";
+	
+	void save() throws IOException;
+	
+	void saveAs(File path) throws IOException;
+	
+	void saveTo(OutputStream out) throws IOException;
+	
+	/**
+	 * gets default package version ranges.
+	 */
+	Properties getDefaultPackageVersions();
+	
+	/**
+	 * gets default package version range for named package.
+	 * Also handles wildcards in defaults.
+	 */
+	String getDefaultPackageVersion(String name);
+	
+	/**
+	 * get project options.
+	 */
+	Properties getOptions();
+	
+	/**
+	 * get project version.
+	 */
+	String getVersion();
+	
+	/**
+	 * gets dependencies (Package-Import and Require-Bundle) needed to compile.
+	 */
+	IBundleModelElement getDependencies();
+	
+	/**
+	 * gets project source directories.
+	 * This is a convenient way to specify bundle contents
+	 * when the project doesn't contains multiple bundles.
+	 */
+	List<String> getSourceDirs();
+	
+	/**
+	 * gets the list of packages represented by getSourceDirs().
+	 * @throws IOException 
+	 */
+	List<String> getSourcePkgs();
+	
+	/**
+	 * gets bundle ids.
+	 */
+	List<String> getBundleIds();
+	
+	/**
+	 * gets bundles.
+	 */
+	List<IBldBundle> getBundles();
+	
+	/**
+	 * convert specified bundle to SigilBundle.
+	 */
+	ISigilBundle getSigilBundle(String id);
+	
+	/**
+	 * convert SigilBundle to specified bundle.
+	 */
+	void setSigilBundle(String id, ISigilBundle sb);
+	
+	/**
+	 * converts default bundle to SigilBundle.
+	 */
+	ISigilBundle getDefaultBundle();
+	
+	/**
+	 * converts SigilBundle to default bundle.
+	 */
+	void setDefaultBundle(ISigilBundle sb);
+	
+	/**
+	 * resolves a relative path against the project file location.
+	 */
+	File resolve(String path);
+	
+	/**
+	 * gets the last modification date of the project file.
+	 */
+	long getLastModified();
+	
+	interface IBldBundle {
+		/**
+		 * gets bundle activator
+		 */
+		String getActivator();
+		
+		/**
+		 * gets bundle id within project.
+		 */
+		String getId();
+		
+		/**
+		 * gets bundle version.
+		 */
+		String getVersion();
+		
+		/**
+		 * gets the Bundle-SymbolicName.
+		 */
+		String getSymbolicName();
+
+		/**
+		 * gets bundles export-packages.
+		 */
+		List<IPackageExport> getExports();
+		
+		/**
+		 * gets project import-packages. 
+		 */
+		List<IPackageImport> getImports();
+		
+		/**
+		 * gets project require-bundles. 
+		 */
+		List<IRequiredBundle> getRequires();
+		
+		/**
+		 * get bundle fragment-host. 
+		 */
+		IRequiredBundle getFragmentHost();
+		
+		/**
+		 * gets bundle libs. 
+		 */
+		Map<String, Map<String, String>> getLibs();
+	
+		/**
+		 * gets the bundle contents
+		 * @return list of package patterns.
+		 */
+		List<String> getContents();
+		
+		/**
+		 * gets the bundle's associated dljar contents.
+		 * This is a convenience which avoids having to define another bundle
+		 * just for the dljar, which is then added to the parent bundle.
+		 * @return list of package patterns.
+		 */
+		List<String> getDownloadContents();
+	
+		/**
+		 * gets SCA composites.
+		 */
+		List<String> getComposites();
+		
+		/**
+		 * gets the additional resources.
+		 * @return map with key as path in bundle, value as path in file system.
+		 * Paths are resolved relative to location of project file and also from classpath.
+		 */
+		Map<String,String> getResources();
+		
+		/**
+		 * gets additional bundle headers.
+		 */
+		Properties getHeaders();
+		
+		/**
+		 * resolves a relative path against the project file location.
+		 */
+		File resolve(String path);
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java
new file mode 100644
index 0000000..edcc1d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.util.Map;
+import java.util.Properties;
+
+public interface IRepositoryConfig {
+	static final String REPOSITORY_PROVIDER = "provider";
+	static final String REPOSITORY_LEVEL = "level";
+
+	/**
+	 * get properties with which to instantiate repositories.
+	 * The key REPOSITORY_PROVIDER will be set to the fully qualified class name of the IRepositoryProvider.
+	 * The key REPOSITORY_LEVEL indicates repository search order.
+	 * @return
+	 */
+	Map<String,Properties> getRepositoryConfig();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java
new file mode 100644
index 0000000..c39e8ab
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.cauldron.bld.core.internal.license.LicenseManager;
+import org.cauldron.bld.core.internal.model.eclipse.DownloadJar;
+import org.cauldron.bld.core.internal.model.eclipse.Library;
+import org.cauldron.bld.core.internal.model.eclipse.LibraryImport;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageExport;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.INewtonSystem;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class BldCore implements BundleActivator {
+	private static LicenseManager licenceManager = new LicenseManager();
+	
+	private static final Logger log = Logger.getLogger(BldCore.class.getName());
+
+	public static void error(String string, Throwable e) {
+		// TODO 
+		log.log( Level.WARNING, string, e );
+	}
+
+	public static void error(String string) {
+		log.log( Level.WARNING, string );
+	}
+
+	public static ILicenseManager getLicenseManager() {
+		return licenceManager;
+	}
+
+	public void start(BundleContext context) throws Exception {
+		init();
+	}
+	
+	public static void init() throws Exception {
+		String uri = "http://sigil.codecauldron.org/xml/sigil-namespace";
+		ModelElementFactory.getInstance().register(ISigilBundle.class,
+				SigilBundle.class, "bundle", "sigil", uri);
+		ModelElementFactory.getInstance().register(IDownloadJar.class,
+				DownloadJar.class, "download", "sigil", uri);
+		ModelElementFactory.getInstance().register(ILibrary.class,
+				Library.class, "library", "sigil", uri);
+		ModelElementFactory.getInstance().register(ILibraryImport.class,
+				LibraryImport.class, "library-import", "sigil", uri);
+		
+		// osgi elements
+		ModelElementFactory.getInstance().register(IBundleModelElement.class,
+				BundleModelElement.class, "bundle", null, null);
+		ModelElementFactory.getInstance().register(IPackageExport.class,
+				PackageExport.class, "package.export", null, null);
+		ModelElementFactory.getInstance().register(IPackageImport.class,
+				PackageImport.class, "package.import", null, null);
+		ModelElementFactory.getInstance().register(IRequiredBundle.class,
+				RequiredBundle.class, "required.bundle", null, null);
+	}
+
+	public void stop(BundleContext context) throws Exception {
+		// TODO Auto-generated method stub
+		
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java
new file mode 100644
index 0000000..456cfc9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.license;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+//import org.cauldron.sigil.model.project.ISigilProjectModel;
+
+public class LicenseManager implements ILicenseManager {
+
+	private HashMap<String, Pattern> licenses = new HashMap<String, Pattern>();
+	private HashMap<String, LicensePolicy> policies = new HashMap<String, LicensePolicy>();
+	private LicensePolicy defaultPolicy = new LicensePolicy(this);
+	
+	public void addLicense(String name, Pattern pattern) {
+		licenses.put( name, pattern );
+	}
+
+	public void removeLicense(String name) {
+		licenses.remove(name);
+	}
+
+	public Set<String> getLicenseNames() {
+		return Collections.unmodifiableSet(licenses.keySet());
+	}
+
+	public Pattern getLicensePattern(String name) {
+		return licenses.get( name );
+	}
+
+	public ILicensePolicy getDefaultPolicy() {
+		return defaultPolicy;
+	}
+
+	//public ILicensePolicy getPolicy(ISigilProjectModel project) {
+	//	synchronized( policies ) {
+	//		LicensePolicy p = policies.get(project.getName());
+	//		
+	//		if ( p == null ) {
+	//			p = new LicensePolicy(this, project);
+	//			policies.put( project.getName(), p );
+	//		}
+	//		
+	//		return p;
+	//	}
+	//}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java
new file mode 100644
index 0000000..4a00b00
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.license;
+
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class LicensePolicy implements ILicensePolicy {
+
+	private LicenseManager licenseManager;
+	
+	public LicensePolicy(LicenseManager licenseManager) {
+		this.licenseManager = licenseManager;
+	}
+
+	public boolean accept(ISigilBundle bundle) {
+		return true;
+	}
+
+	public void addAllowed(String licenseName) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void removeAllowed(String licenseName) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void save(IProgressMonitor monitor) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java
new file mode 100644
index 0000000..f8bc392
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.eclipse.core.runtime.IPath;
+
+public class DownloadJar extends AbstractCompoundModelElement implements IDownloadJar {
+
+	private static final long serialVersionUID = 1L;
+
+	private Set<IPath> entries = new HashSet<IPath>();
+	
+	public DownloadJar() {
+		super("RMI Classpath Download Jar");
+	}
+	
+	public void addEntry(IPath entry) {
+		entries.add( entry );
+	}
+	
+	public void removeEntry(IPath entry) {
+		entries.remove( entry );
+	}
+	
+	public Set<IPath> getEntrys() {
+		return entries;
+	}
+
+	public void clearEntries() {
+		entries.clear();
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java
new file mode 100644
index 0000000..9ce6be8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public class Library extends AbstractCompoundModelElement implements ILibrary {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String name;
+	private Version version;
+	private Set<IRequiredBundle> bundles;
+	private Set<IPackageImport> imports;
+	
+	public Library() {
+		super("Library");
+		bundles = new HashSet<IRequiredBundle>();
+		imports = new HashSet<IPackageImport>();
+	}
+	
+	public void addBundle(IRequiredBundle bundle) {
+		bundles.add(bundle);
+	}
+
+	public void addImport(IPackageImport pi) {
+		imports.add(pi);
+	}
+
+	public Collection<IRequiredBundle> getBundles() {
+		return bundles;
+	}
+
+	public Collection<IPackageImport> getImports() {
+		return imports;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public Version getVersion() {
+		return version;
+	}
+
+	public void removeBundle(IRequiredBundle bundle) {
+		bundles.remove(bundle);
+	}
+
+	public void removeImport(IPackageImport pi) {
+		imports.remove(pi);
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setVersion(Version version) {
+		this.version = version;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java
new file mode 100644
index 0000000..2c86944
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+
+public class LibraryImport extends AbstractModelElement implements ILibraryImport {
+
+	private static final long serialVersionUID = 1L;
+
+	public LibraryImport() {
+		super("Library Import");
+	}
+
+	private String libraryName;
+	private VersionRange range = VersionRange.ANY_VERSION;
+	
+	public String getLibraryName() {
+		return libraryName;
+	}
+
+	public VersionRange getVersions() {
+		return range;
+	}
+
+	public void setLibraryName(String name) {
+		this.libraryName = name;
+	}
+
+	public void setVersions(VersionRange range) {
+		this.range = range;
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java
new file mode 100644
index 0000000..1e09fcc
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilBundle extends AbstractCompoundModelElement implements ISigilBundle {
+    
+	private static final long serialVersionUID = 1L;
+	
+    private IBundleModelElement bundle;
+    private IDownloadJar download;
+    private Set<IPath> sourcePaths;
+    private Set<IPath> libraryPaths;
+    private Set<ISCAComposite> composites;
+    private Set<String> classpath;
+    private Set<String> packages;
+    private Set<String> dlPackages;
+    private IPath location;
+
+	private IPath sourcePathLocation;
+	private IPath licencePathLocation;
+	private IPath sourceRootPath;
+	private String bundleHost;
+    
+    public SigilBundle() {
+    	super( "Sigil Bundle" );
+        sourcePaths = new HashSet<IPath>();
+        libraryPaths = new HashSet<IPath>();
+        composites = new HashSet<ISCAComposite>();
+        classpath = new HashSet<String>();
+        packages = new HashSet<String>();
+        dlPackages = new HashSet<String>();
+    }
+            
+    public void synchronize(IProgressMonitor monitor) throws IOException {
+    	SubMonitor progress = SubMonitor.convert(monitor, 100);
+    	progress.subTask("Synchronizing " + bundle.getSymbolicName() + " binary" );
+    	sync(location, bundle.getUpdateLocation(), progress.newChild(45));
+    	
+    	try {
+        	progress.subTask("Synchronizing " + bundle.getSymbolicName() + " source" );
+			sync(sourcePathLocation, bundle.getSourceLocation(), progress.newChild(45));
+		} catch (IOException e) {
+			BldCore.error( "Failed to download source for " + bundle.getSymbolicName() + " " + bundle.getVersion(), e.getCause() );
+		}
+		
+    	try {
+        	progress.subTask("Synchronizing " + bundle.getSymbolicName() + " licence" );
+			sync(licencePathLocation, bundle.getLicenseURI(), progress.newChild(10));
+		} catch (IOException e) {
+			BldCore.error( "Failed to download licence for " + bundle.getSymbolicName() + " " + bundle.getVersion(), e.getCause() );
+		}
+	}
+
+	public boolean isSynchronized() {
+		return location == null || location.toFile().exists();
+	}
+
+	private static void sync(IPath local, URI remote, IProgressMonitor monitor) throws IOException {
+		try {
+	    	if ( local != null && !local.toFile().exists() ) {
+	    		if ( remote != null ) {
+	    			URL url = remote.toURL();
+	    			URLConnection connection = url.openConnection();
+	    			int contentLength = connection.getContentLength();
+	    			
+	    			monitor.beginTask("Downloading from " + url.getHost(), contentLength);
+	    			
+	    			InputStream in = null;
+	    			OutputStream out = null;
+	    			try {
+	    				URLConnection conn = url.openConnection();
+	    				if ( conn instanceof HttpURLConnection ) {
+	    					HttpURLConnection http = (HttpURLConnection) conn;
+	    					http.setConnectTimeout(10000);
+	    					http.setReadTimeout(5000);
+	    				}
+	    				in = conn.getInputStream();
+	    				File f = local.toFile();
+	    				f.getParentFile().mkdirs();
+	    				out = new FileOutputStream( f );
+	    				stream( in, out, monitor );
+	    			}
+	    			finally {
+	    				if ( in != null ) {
+	    					in.close();
+	    				}
+	    				if ( out != null ) {
+	    					out.close();
+	    				}
+	    				monitor.done();
+	    			}
+	    		}
+	    	}
+		}
+		catch (IOException e) {
+			local.toFile().delete();
+			throw e;
+		}
+	}
+
+	private static void stream(InputStream in, OutputStream out, IProgressMonitor monitor) throws IOException {
+		byte[] b = new byte[1024];
+		for ( ;; ) {
+			if ( monitor.isCanceled() ) {
+				throw new InterruptedIOException( "User canceled download" );
+			}
+			int r = in.read( b );
+			if ( r == -1 ) break;
+			out.write(b, 0, r);
+			monitor.worked(r);
+		}
+		
+		out.flush();
+	}
+    
+    public IBundleModelElement getBundleInfo() {
+        return bundle;
+    }
+    
+    public void setBundleInfo(IBundleModelElement bundle) {
+    	if ( bundle == null ) {
+    		if (this.bundle != null) {
+    			this.bundle.setParent(null);
+    		}
+    	}
+    	else {
+    		bundle.setParent(this);
+    	}
+        this.bundle = bundle;
+    }
+    
+    public IDownloadJar getDownloadJar() {
+    	return download;
+    }
+    
+    public void setDownloadJar(IDownloadJar download) {
+    	this.download = download;
+    }
+        
+    public void addLibraryPath( IPath path ) {
+        libraryPaths.add( path );
+    }
+    
+    public void removeLibraryPath( IPath path ) {
+        libraryPaths.remove( path );
+    }
+    
+    public Set<IPath> getLibraryPaths() {
+        return libraryPaths;
+    }
+    
+    public void addSourcePath( IPath path ) {
+        sourcePaths.add( path );
+    }
+    
+    public void removeSourcePath( IPath path ) {
+        sourcePaths.remove( path );
+    }
+    
+    public Set<IPath> getSourcePaths() {
+        return sourcePaths;
+    }
+
+    public void clearSourcePaths() {
+    	sourcePaths.clear();
+	}
+    
+	public void addComposite(ISCAComposite composite) {
+        composites.add( composite );
+        composite.setParent(this);
+    }
+
+    public Set<ISCAComposite> getComposites() {
+        return composites;
+    }
+
+    public void removeComposite(ISCAComposite composite) {
+        if ( composites.remove( composite ) ) {
+        	composite.setParent(null);
+        }
+    }
+	public void addClasspathEntry(String encodedClasspath) {
+		classpath.add( encodedClasspath.trim() );
+	}
+	
+	public Set<String> getClasspathEntrys() {
+		return classpath;
+	}
+	
+	public void removeClasspathEntry(String encodedClasspath) {
+		classpath.remove(encodedClasspath.trim());
+	}
+
+	public IPath getLocation() {
+		return location;
+	}
+	
+	public void setLocation(IPath location) {
+		this.location = location;
+	}
+	
+	public IPath getSourcePathLocation() {
+		return sourcePathLocation;
+	}
+	public IPath getSourceRootPath() {
+		return sourceRootPath;
+	}
+	public void setSourcePathLocation(IPath location) {
+		this.sourcePathLocation = location;
+	}
+	public void setSourceRootPath(IPath location) {
+		this.sourceRootPath = location;
+	}
+		
+	@Override
+	public String toString() {
+		return "SigilBundle[" + (getBundleInfo() == null ? null : (getBundleInfo().getSymbolicName() + ":" + getBundleInfo().getVersion())) + "]";
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if ( obj == null ) return false;
+		if ( obj == this ) return true;
+		
+		if ( obj instanceof SigilBundle) {
+			return obj.toString().equals( toString() );
+		}
+		
+		return false;
+	}
+	@Override
+	public int hashCode() {
+		return 31 * toString().hashCode();
+	}
+
+	public IPath getLicencePathLocation() {
+		return licencePathLocation;
+	}
+
+	public void setLicencePathLocation(IPath licencePathLocation) {
+		this.licencePathLocation = licencePathLocation;
+	}
+    
+	public String getFragmentHost() {
+		return bundleHost;
+	}
+
+	public void setFragmentHost(String symbolicName) {
+		this.bundleHost = symbolicName;
+	}
+
+	public String getElementName() {
+		return bundle.getSymbolicName();
+	}
+
+	public Version getVersion() {
+		return bundle.getVersion();
+	}
+
+	public void setVersion(Version version) {
+		this.bundle.setVersion(version);
+	}
+	
+	public String getSymbolicName() {
+		return bundle.getSymbolicName();
+	}
+
+	public Set<String> getPackages() {
+		return packages;
+	}
+
+	public void addPackage(String pkg) {
+		packages.add(pkg);
+	}
+
+	public boolean removePackage(String pkg) {
+		return packages.remove(pkg);
+	}
+	
+	public Set<String> getDownloadPackages() {
+		return dlPackages;
+	}
+
+	public void addDownloadPackage(String pkg) {
+		dlPackages.add(pkg);
+	}
+
+	public boolean removeDownloadPackage(String pkg) {
+		return dlPackages.remove(pkg);
+	}
+
+	public IPackageExport findExport(String packageName) {
+		for ( IPackageExport e : bundle.getExports() ) {
+			if ( packageName.equals( e.getPackageName() ) ) {
+				return e;
+			}
+		}
+		return null;
+	}
+
+	public IPackageImport findImport(String packageName) {
+		for ( IPackageImport i : bundle.getImports() ) {
+			if ( packageName.equals( i.getPackageName() ) ) {
+				return i;
+			}
+		}
+		return null;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java
new file mode 100644
index 0000000..f4024ef
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.InvalidModelException;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public class BundleModelElement extends AbstractCompoundModelElement implements IBundleModelElement {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    // required obr values
+    private URI updateLocation;
+    private String symbolicName;
+    private Version version = Version.emptyVersion;
+    private Set<IPackageImport> imports;
+    private Set<IPackageExport> exports;
+    private Set<IRequiredBundle> requires;
+    private URI sourceLocation;
+    private Set<String> classpathElements;
+    private IRequiredBundle fragmentHost;
+
+    // human readable values
+    private String name;
+    private String description;
+    private String category;
+    private URI licenseURI;
+    private URI docURI;
+    private String vendor;
+    private String contactAddress;
+    private String copyright;
+    
+    // internal values
+    private String activator;
+    private Set<ILibraryImport> libraries;
+
+    public BundleModelElement() {
+    	super( "OSGi Bundle" );
+        this.imports = new HashSet<IPackageImport>();
+        this.exports = new HashSet<IPackageExport>();
+        this.requires = new HashSet<IRequiredBundle>();
+        this.classpathElements = new HashSet<String>();
+        this.libraries = new HashSet<ILibraryImport>();
+    }
+
+	public String getActivator() {
+		return activator;
+	}
+
+	public void setActivator(String activator) {
+		this.activator = activator;
+	}
+	
+	public void addLibraryImport(ILibraryImport library) {
+    	libraries.add(library);
+	}
+
+	public Set<ILibraryImport> getLibraryImports() {
+		return libraries;
+	}
+
+	public void removeLibraryImport(ILibraryImport library) {
+		libraries.remove(library);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getCategory()
+	 */
+    public String getCategory() {
+        return category;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setCategory(java.lang.String)
+	 */
+    public void setCategory(String category) {
+        this.category = category;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getContactAddress()
+	 */
+    public String getContactAddress() {
+        return contactAddress;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setContactAddress(java.lang.String)
+	 */
+    public void setContactAddress(String contactAddress) {
+        this.contactAddress = contactAddress;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getCopyright()
+	 */
+    public String getCopyright() {
+        return copyright;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setCopyright(java.lang.String)
+	 */
+    public void setCopyright(String copyright) {
+        this.copyright = copyright;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getDocURI()
+	 */
+    public URI getDocURI() {
+        return docURI;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setDocURI(java.net.URI)
+	 */
+    public void setDocURI(URI docURI) {
+        this.docURI = docURI;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getExports()
+	 */
+    public Set<IPackageExport> getExports() {
+        return exports;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addExport(org.cauldron.sigil.model.osgi.PackageExport)
+	 */
+    public void addExport(IPackageExport packageExport) {
+        exports.add(packageExport);
+        packageExport.setParent(this);
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeExport(org.cauldron.sigil.model.osgi.PackageExport)
+	 */
+    public void removeExport(IPackageExport packageExport) {
+    	if ( exports.remove(packageExport) ) {
+    		packageExport.setParent(null);
+    	}
+    }
+    
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getImports()
+	 */
+    public Set<IPackageImport> getImports() {
+        return imports;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addImport(org.cauldron.sigil.model.osgi.PackageImport)
+	 */
+    public void addImport(IPackageImport packageImport) {
+        imports.add(packageImport);
+        packageImport.setParent(this);
+    }
+    
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeImport(org.cauldron.sigil.model.osgi.PackageImport)
+	 */
+    public void removeImport(IPackageImport packageImport) {
+    	if ( imports.remove( packageImport ) ) {
+    		packageImport.setParent(null);
+    	}
+    }
+    
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getRequiredBundles()
+	 */
+    public Set<IRequiredBundle> getRequiredBundles() {
+        return requires;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addRequiredBundle(org.cauldron.sigil.model.osgi.RequiresBundle)
+	 */
+    public void addRequiredBundle(IRequiredBundle bundle) {
+        requires.add( bundle );
+        bundle.setParent(this);
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeRequiredBundle(org.cauldron.sigil.model.osgi.RequiresBundle)
+	 */
+    public void removeRequiredBundle(IRequiredBundle bundle) {
+    	if ( requires.remove(bundle) ) {
+    		bundle.setParent(null);
+    	}
+    }
+    
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getLicenseURI()
+	 */
+    public URI getLicenseURI() {
+        return licenseURI;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setLicenseURI(java.net.URI)
+	 */
+    public void setLicenseURI(URI licenseURI) {
+        this.licenseURI = licenseURI;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getSourceLocation()
+	 */
+    public URI getSourceLocation() {
+        return sourceLocation;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setSourceLocation(java.net.URI)
+	 */
+    public void setSourceLocation(URI sourceLocation) {
+        this.sourceLocation = sourceLocation;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getSymbolicName()
+	 */
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setSymbolicName(java.lang.String)
+	 */
+    public void setSymbolicName(String symbolicName) {
+        this.symbolicName = symbolicName == null ? null : symbolicName.intern();
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getUpdateLocation()
+	 */
+    public URI getUpdateLocation() {
+        return updateLocation;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setUpdateLocation(java.net.URI)
+	 */
+    public void setUpdateLocation(URI updateLocation) {
+        this.updateLocation = updateLocation;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getVendor()
+	 */
+    public String getVendor() {
+    		return vendor;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setVendor(java.lang.String)
+	 */
+    public void setVendor(String vendor) {
+        this.vendor = vendor;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getVersion()
+	 */
+    public Version getVersion() {
+    	return version;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setVersion(java.lang.String)
+	 */
+    public void setVersion(Version version) {
+        this.version = version == null ? Version.emptyVersion : version;
+    }
+
+    public void checkValid() throws InvalidModelException {
+        if (symbolicName == null)
+            throw new InvalidModelException(this, "Bundle symbolic name not set");
+    }
+
+    public BundleModelElement clone() {
+        BundleModelElement bd = (BundleModelElement) super.clone();
+
+        bd.imports = new HashSet<IPackageImport>();
+        bd.exports = new HashSet<IPackageExport>();
+        bd.requires = new HashSet<IRequiredBundle>();
+        
+        for (IPackageImport pi : imports ) {
+            bd.imports.add((IPackageImport) pi.clone());
+        }
+
+        for (IPackageExport pe : exports ) {
+            bd.exports.add((IPackageExport) pe.clone());
+        }
+        
+        for ( IRequiredBundle rb : requires ) {
+            bd.requires.add((IRequiredBundle) rb.clone());
+        }
+
+        return bd;
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append("BundleModelElement[");
+        buf.append(symbolicName);
+        buf.append(", ");
+        buf.append(version);
+        buf.append("]");
+
+        return buf.toString();
+    }
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public void addClasspath(String path) {
+		classpathElements.add( path );
+	}
+
+	public Collection<String> getClasspaths() {
+		return classpathElements.isEmpty() ? Collections.singleton( "." ) : classpathElements;
+	}
+
+	public void removeClasspath(String path) {
+		classpathElements.remove( path );
+	}
+
+	public IRequiredBundle getFragmentHost() {
+		return fragmentHost;
+	}
+
+	public void setFragmentHost(IRequiredBundle fragmentHost) {
+		this.fragmentHost = fragmentHost;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java
new file mode 100644
index 0000000..536dbaf
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.osgi.framework.Version;
+
+public class PackageExport extends AbstractModelElement implements IPackageExport {
+
+	private static final long serialVersionUID = 1L;
+
+	private String name;
+    private Version version;
+    private HashSet<String> uses = new HashSet<String>();
+
+	public PackageExport() {
+		super("OSGi Package Export");
+	}
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#getPackageName()
+	 */
+    public String getPackageName() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#setPackageName(java.lang.String)
+	 */
+    public void setPackageName(String packageName) {
+        this.name = packageName;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#getVersion()
+	 */
+    public Version getVersion() {
+    	Version result;
+        if(version != null) {
+        	result = version;
+        } else {
+	        ISigilBundle owningBundle = getAncestor(ISigilBundle.class);
+	        if(owningBundle == null) {
+	        	result = Version.emptyVersion;
+	        } else {
+	        	result = owningBundle.getVersion();
+	        }
+        }
+        return result;
+    }
+    
+    public Version getRawVersion() {
+    	return version;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#setVersion(java.lang.String)
+	 */
+    public void setVersion(Version version) {
+        this.version = version; // == null ? Version.emptyVersion : version;
+    }
+    
+    public void addUse(String use) {
+    	uses.add(use);
+	}
+
+	public Collection<String> getUses() {
+		return uses;
+	}
+
+	public void removeUse(String use) {
+		uses.remove(use);
+	}
+
+	@Override
+    public String toString() {
+    	return "PackageExport[" + name + ":" + version + ":uses=" + uses + "]";
+    }
+
+	public void setUses(Collection<String> uses) {
+		this.uses.clear();
+		this.uses.addAll(uses);
+	}
+
+	public int compareTo(IPackageExport o) {
+		int i = name.compareTo(o.getPackageName());
+		
+		if ( i == 0 ) {
+			i = compareVersion(o.getVersion());
+		}
+		
+		return i;
+	}
+
+	private int compareVersion(Version other) {
+		if ( version == null ) {
+			if ( other == null ) {
+				return 0;
+			}
+			else {
+				return 1;
+			}
+		}
+		else {
+			if ( other == null ) {
+				return -1;
+			}
+			else {
+				return version.compareTo(other);
+			}
+		}
+	}
+	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java
new file mode 100644
index 0000000..a9319f3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.InvalidModelException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class PackageImport extends AbstractModelElement implements IPackageImport {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String name;
+    private VersionRange versions = VersionRange.ANY_VERSION;
+    
+    // resolution directive
+    private boolean optional;
+	private boolean dependency = true;
+	private OSGiImport osgiImport = OSGiImport.AUTO;
+
+    public PackageImport() {
+    	super( "OSGi Package Import" );
+    }
+
+    @Override
+	public void checkValid() throws InvalidModelException {
+    	if ( name == null ) {
+    		throw new InvalidModelException( this, "Package name must be set" );
+    	}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#isOptional()
+	 */
+    public boolean isOptional() {
+        return optional;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setOptional(boolean)
+	 */
+    public void setOptional(boolean optional) {
+        this.optional = optional;
+    }
+
+	public boolean isDependency() {
+		return dependency;
+	}
+
+	public void setDependency(boolean dependency) {
+		this.dependency  = dependency;
+	}
+    
+	public OSGiImport getOSGiImport() {
+		return osgiImport;
+	}
+
+	public void setOSGiImport(OSGiImport osgiHeader) {
+		this.osgiImport = osgiHeader;
+	}
+
+    public String getPackageName() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setName(java.lang.String)
+	 */
+    public void setPackageName(String name) {
+        this.name = name;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#getVersion()
+	 */
+    public VersionRange getVersions() {
+        return versions;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setVersion(java.lang.String)
+	 */
+    public void setVersions(VersionRange versions) {
+        this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+    }
+
+	@Override
+	public String toString() {
+		return "Package-Import[" + name + ":" + versions + ":" + (optional ? "optional" : "mandatory") + "]";
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        
+		if ( obj instanceof PackageImport ) {
+			PackageImport pi = (PackageImport) obj;
+			return name.equals( pi.name ) && versions.equals( pi.versions ) && optional == pi.optional;
+		}
+		else {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		int hc = name.hashCode() * versions.hashCode();
+		
+		if ( optional ) {
+			hc *= -1;
+		}
+		
+		return hc;
+	}
+
+	public boolean accepts(IModelElement provider) {
+		if ( provider instanceof IPackageExport ) {
+			IPackageExport pe = (IPackageExport) provider;
+			return pe.getPackageName().equals( name ) && versions.contains( pe.getVersion() );
+		}
+		else {
+			return false;
+		}
+	}
+
+	public int compareTo(IPackageImport o) {
+		int i = name.compareTo(o.getPackageName());
+		
+		if ( i == 0 ) {
+			i = compareVersion(o.getVersions());
+		}
+		
+		return i;
+	}
+
+	private int compareVersion(VersionRange range) {
+		if ( versions == null ) {
+			if ( range == null ) {
+				return 0;
+			}
+			else {
+				return 1;
+			}
+		}
+		else {
+			if ( range == null ) {
+				return -1;
+			}
+			else {
+				return versions.getCeiling().compareTo(range.getCeiling());
+			}
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java
new file mode 100644
index 0000000..35c3f7b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class RequiredBundle extends AbstractModelElement implements IRequiredBundle {
+	private static final long serialVersionUID = 1L;
+	
+	private String symbolicName;
+    private VersionRange versions = VersionRange.ANY_VERSION;
+    private boolean optional;
+    
+	public RequiredBundle() {
+		super("OSGi Bundle Requirement");
+	}
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#getSymbolicName()
+	 */
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#setSymbolicName(java.lang.String)
+	 */
+    public void setSymbolicName(String symbolicName) {
+        this.symbolicName = symbolicName == null ? null : symbolicName.intern();
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#getVersion()
+	 */
+    public VersionRange getVersions() {
+        return versions;
+    }
+
+    /* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#setVersion(java.lang.String)
+	 */
+    public void setVersions(VersionRange versions) {
+        this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+    }
+
+	public boolean isOptional() {
+		return optional;
+	}
+
+	public void setOptional(boolean optional) {
+		this.optional = optional;
+	}
+	
+	@Override
+	public String toString() {
+		return "RequiredBundle[" + symbolicName + ":" + versions + ":" + (optional ? "optional" : "mandatory") + "]";
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        
+		if ( obj instanceof RequiredBundle ) {
+			RequiredBundle rb = (RequiredBundle) obj;
+			return symbolicName.equals( rb.symbolicName ) && versions.equals( rb.versions ) && optional == rb.optional;
+		}
+		else {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		int hc = symbolicName.hashCode() * versions.hashCode();
+		
+		if ( optional ) {
+			hc *= -1;
+		}
+		
+		return hc;
+	}
+
+	public boolean accepts(IModelElement provider) {
+		if ( provider instanceof IBundleModelElement ) {
+			IBundleModelElement bndl = (IBundleModelElement) provider;
+			return symbolicName.equals( bndl.getSymbolicName() ) && versions.contains( bndl.getVersion() );
+		}
+		else {
+			return false;
+		}
+	}
+	
+	public int compareTo(IRequiredBundle o) {
+		int i = symbolicName.compareTo(o.getSymbolicName());
+		
+		if ( i == 0 ) {
+			i = compareVersion(o.getVersions());
+		}
+		
+		return i;
+	}
+
+	private int compareVersion(VersionRange range) {
+		if ( versions == null ) {
+			if ( range == null ) {
+				return 0;
+			}
+			else {
+				return 1;
+			}
+		}
+		else {
+			if ( range == null ) {
+				return -1;
+			}
+			else {
+				return versions.getCeiling().compareTo(range.getCeiling());
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java
new file mode 100644
index 0000000..9157288
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.licence;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+//import org.cauldron.sigil.model.project.ISigilProjectModel;
+
+public interface ILicenseManager {
+	void addLicense(String name, Pattern pattern);
+	void removeLicense(String name);
+	Set<String> getLicenseNames();
+	Pattern getLicensePattern(String name);
+	ILicensePolicy getDefaultPolicy();
+	//ILicensePolicy getPolicy(ISigilProjectModel project);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java
new file mode 100644
index 0000000..a5bcdac
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.licence;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface ILicensePolicy {
+	void addAllowed(String licenseName);
+	void removeAllowed(String licenseName);
+	boolean accept(ISigilBundle bundle);
+	void save(IProgressMonitor monitor);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java
new file mode 100644
index 0000000..57d859a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java
@@ -0,0 +1,444 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.bld.core.BldCore;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.osgi.framework.Version;
+
+public class BundleResolver implements IBundleResolver {
+
+	private class BundleOrderComparator implements Comparator<ISigilBundle> {
+		private IModelElement requirement;
+		
+		public BundleOrderComparator(IModelElement requirement) {
+			this.requirement = requirement;
+		}
+
+		public int compare(ISigilBundle o1, ISigilBundle o2) {
+			int c = compareVersions(o1, o2);
+			
+			if ( c == 0 ) {
+				c = compareImports(o1, o2);
+			}
+			
+			return c;
+		}
+
+		private int compareImports(ISigilBundle o1, ISigilBundle o2) {
+			int c1 = o1.getBundleInfo().getImports().size();
+			int c2 = o2.getBundleInfo().getImports().size();
+			
+			if ( c1 < c2 ) {
+				return -1;
+			}
+			else if ( c2 > c1 ) {
+				return 1;
+			}
+			else {
+				return 0;
+			}
+		}
+
+		private int compareVersions(ISigilBundle o1, ISigilBundle o2) {
+			Version v1 = null;
+			Version v2 = null;
+			if ( requirement instanceof IPackageImport ) {
+				v1 = findExportVersion( (IPackageImport) requirement, o1 );
+				v2 = findExportVersion( (IPackageImport) requirement, o2 );
+			}
+			else if ( requirement instanceof IRequiredBundle ) {
+				v1 = o1.getBundleInfo().getVersion();
+				v2 = o1.getBundleInfo().getVersion();
+			}
+			
+			if ( v1 == null ) {
+				if ( v2 == null ) {
+					return 0;
+				}
+				else {
+					return 1;
+				}
+			}
+			else {
+				if ( v2 == null ) {
+					return -1;
+				}
+				else {
+					return v2.compareTo(v1);
+				}
+			}
+		}
+
+		private Version findExportVersion(IPackageImport pi, ISigilBundle o1) {
+			for ( IPackageExport pe : o1.getBundleInfo().getExports() ) {
+				if ( pi.getPackageName().equals( pi.getPackageName() ) ) {
+					return pe.getVersion();
+				}
+			}
+			
+			return null;
+		}
+
+	}
+
+	private class ResolutionContext {
+		private final IModelElement root;
+		private final ResolutionConfig config;
+		private final IResolutionMonitor monitor;
+		
+		private final Resolution resolution = new Resolution();
+		private final Set<IModelElement> parsed = new HashSet<IModelElement>();
+		private final LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();
+		
+		public ResolutionContext(IModelElement root, ResolutionConfig config, IResolutionMonitor monitor) {
+			this.root = root;
+			this.config = config;
+			this.monitor = monitor;
+		}
+
+		public void enterModelElement(IModelElement element) {
+			parsed.add(element);
+		}
+		
+		public void exitModelElement(IModelElement element) {
+			parsed.remove(element);
+		}
+		
+		public boolean isNewModelElement(IModelElement element) {
+			return !parsed.contains(element);
+		}
+		
+		public boolean isValid() {
+			return resolution.isSuccess();
+		}
+
+		public void setValid(boolean valid) {
+			resolution.setSuccess(valid);
+		}
+
+		public ResolutionException newResolutionException() {
+			return new ResolutionException(root, requirements.toArray( new IModelElement[requirements.size()]) );
+		}
+
+		public void startRequirement(IModelElement element) {
+			requirements.add(element);
+			monitor.startResolution(element);
+		}
+		
+		public void endRequirement(IModelElement element) {
+			ISigilBundle provider = resolution.getProvider(element);
+			
+			setValid( provider != null || isOptional(element) || config.isIgnoreErrors() );
+		
+			if ( isValid() ) {
+				// only clear stack if valid
+				// else use it as an aid to trace errors
+				requirements.remove(element);
+			}
+			
+			monitor.endResolution( element, provider );	
+		}		
+	}
+	
+	private class Resolution implements IResolution {
+		private Map<ISigilBundle, List<IModelElement>> providees = new HashMap<ISigilBundle, List<IModelElement>>();
+		private Map<IModelElement, ISigilBundle> providers = new HashMap<IModelElement, ISigilBundle>();
+		private boolean success = true; // assume success
+		
+		boolean addProvider(IModelElement element, ISigilBundle provider) {
+			providers.put( element, provider );
+			
+			List<IModelElement> requirements = providees.get( provider );
+			
+			boolean isNewProvider = requirements == null;
+			
+			if ( isNewProvider ) {
+				requirements = new ArrayList<IModelElement>();
+				providees.put( provider, requirements );
+			}
+			
+			requirements.add( element );
+			
+			return isNewProvider;
+		}
+		
+		void removeProvider(IModelElement element, ISigilBundle provider) {
+			providers.remove(element);
+			List<IModelElement> e = providees.get(provider);
+			e.remove(element);
+			if ( e.isEmpty() ) {
+				providees.remove(provider);
+			}
+		}
+		
+		void setSuccess(boolean success) {
+			this.success = success;
+		}
+
+		public boolean isSuccess() {
+			return success;
+		}
+		
+		public ISigilBundle getProvider(IModelElement requirement) {
+			return providers.get(requirement);
+		}
+		
+		public Set<ISigilBundle> getBundles() {
+			return providees.keySet();
+		}
+
+		public List<IModelElement> getMatchedRequirements(ISigilBundle bundle) {
+			return providees.get(bundle);
+		}
+		
+		public boolean isSynchronized() {
+			for ( ISigilBundle b : getBundles() ) {
+				if ( !b.isSynchronized() ) {
+					return false;
+				}
+			}
+			
+			return true;
+		}
+		
+		public void synchronize(IProgressMonitor monitor) {
+			Set<ISigilBundle> bundles = getBundles();
+			SubMonitor progress = SubMonitor.convert(monitor, bundles.size());
+			
+			for ( ISigilBundle b : bundles ) {
+				if ( monitor.isCanceled() ) {
+					break;
+				}
+				
+				try {
+					b.synchronize(progress.newChild(1));
+				} catch (IOException e) {
+					BldCore.error( "Failed to synchronize " + b, e );
+				}
+			}
+		}
+	}
+
+	private static final IResolutionMonitor NULL_MONITOR = new IResolutionMonitor() {
+		public void endResolution(IModelElement requirement,
+				ISigilBundle sigilBundle) {
+		}
+
+		public boolean isCanceled() {
+			return false;
+		}
+
+		public void startResolution(IModelElement requirement) {
+		}		
+	};
+	
+	private IRepositoryManager repositoryManager;
+	
+	public BundleResolver(IRepositoryManager repositoryManager) {
+		this.repositoryManager = repositoryManager;
+	}
+
+	public IResolution resolve(IModelElement element, ResolutionConfig config, IResolutionMonitor monitor) throws ResolutionException {
+		if ( monitor == null ) {
+			monitor = NULL_MONITOR;
+		}
+		ResolutionContext ctx = new ResolutionContext(element, config, monitor);
+
+		resolveElement(element, ctx);
+		
+		if ( !ctx.isValid() ) {
+			throw ctx.newResolutionException();
+		}
+		
+		return ctx.resolution;
+	}
+
+	private void resolveElement(IModelElement element, ResolutionContext ctx) throws ResolutionException {
+		if ( isRequirement(element) ) {
+			resolveRequirement(element, ctx);
+		}
+		
+		if ( ctx.isValid() && element instanceof ICompoundModelElement ) {
+			resolveCompound((ICompoundModelElement) element, ctx);
+		}
+	}
+
+	private void resolveCompound(ICompoundModelElement compound, ResolutionContext ctx) throws ResolutionException {
+		for ( IModelElement element : compound.children() ) {
+			if ( ctx.isNewModelElement(element) ) {
+				if ( isRequirement(element) ) {
+					resolveRequirement(element, ctx);
+				}
+				else if ( element instanceof ICompoundModelElement ) {
+					if ( !ctx.monitor.isCanceled() ) {
+						ctx.enterModelElement( element );
+						resolveElement((ICompoundModelElement) element, ctx);	
+						ctx.exitModelElement(element);
+					}
+				}
+
+				if ( !ctx.isValid() ) {
+					break;					
+				}
+			}
+		}
+	}
+
+	private void resolveRequirement(IModelElement requirement, ResolutionContext ctx) throws ResolutionException {
+		if ( ctx.config.isOptional() || !isOptional(requirement) ) {
+			ctx.startRequirement(requirement );
+	
+			try {
+				int[] priorities = repositoryManager.getPriorityLevels();
+				
+				outer: for ( int i = 0; i< priorities.length; i++ ) {
+					List<ISigilBundle> providers = findProvidersAtPriority(priorities[i], requirement, ctx);
+					
+					if ( !providers.isEmpty() && !ctx.monitor.isCanceled() ) {
+						if ( providers.size() > 1 ) {
+							Collections.sort(providers, new BundleOrderComparator(requirement));
+						}
+		
+						for ( ISigilBundle provider : providers ) {
+							// reset validity - if there's another provider it can still be solved
+							ctx.setValid(true);
+							if ( ctx.resolution.addProvider(requirement, provider) ) {
+								if ( ctx.config.isDependents() ) {
+									resolveElement(provider, ctx);
+								}
+								
+								if ( ctx.isValid() ) {
+									break outer;
+								}
+								else {
+									ctx.resolution.removeProvider(requirement, provider);
+								}
+							}
+							else {
+								break outer;
+							}
+						}
+					}
+				}
+			}
+			finally {
+				ctx.endRequirement(requirement);
+			}
+		}
+	}
+
+	private List<ISigilBundle> findProvidersAtPriority(int i, IModelElement requirement, ResolutionContext ctx) throws ResolutionException {
+		ArrayList<ISigilBundle> providers = new ArrayList<ISigilBundle>();
+		
+		for (IBundleRepository rep : repositoryManager.getRepositories(i)) {
+			if ( ctx.monitor.isCanceled() ) {
+				break;
+			}
+			providers.addAll( findProviders( requirement, ctx.config, rep ) );
+		}
+		
+		return providers;
+	}
+
+	private Collection<ISigilBundle> findProviders(IModelElement requirement, ResolutionConfig config, IBundleRepository rep) throws ResolutionException {
+		ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		if ( requirement instanceof IPackageImport ) {
+			IPackageImport pi = (IPackageImport) requirement;
+			found.addAll( rep.findAllProviders( pi, config.getOptions() ) );
+		}
+		else if ( requirement instanceof IRequiredBundle ) {
+			IRequiredBundle rb = (IRequiredBundle) requirement;
+			found.addAll( rep.findAllProviders( rb, config.getOptions() ) );
+		}
+		else if ( requirement instanceof ILibraryImport ) {
+			ILibrary lib = repositoryManager.resolveLibrary((ILibraryImport) requirement);
+			if (lib != null) {
+				found.addAll( rep.findProviders(lib, config.getOptions()) );
+			}
+		}
+		else {
+			// shouldn't get here - developer error if do
+			// use isRequirement before getting anywhere near this logic...
+			throw new IllegalStateException( "Invalid requirement type " + requirement );
+		}
+
+		return found;
+	}
+	
+
+	private boolean isOptional(IModelElement element) {
+		if ( element instanceof IPackageImport ) {
+			return ((IPackageImport) element).isOptional();
+		}
+		else if ( element instanceof IRequiredBundle ) {
+			return ((IRequiredBundle) element).isOptional();
+		}
+		else if ( element instanceof ILibraryImport ) {
+			ILibrary lib = repositoryManager.resolveLibrary((ILibraryImport) element);
+			for ( IPackageImport pi : lib.getImports() ) {
+				if ( !isOptional(pi) ) {
+					return false;
+				}
+			}
+			return true;
+		}
+		else {
+			// should never get this due to isRequirement test prior to calling this
+			// developer error if found
+			throw new IllegalStateException( "Invalid optional element test for " + element);
+		}
+	}
+
+	private boolean isRequirement(IModelElement element) {
+		return element instanceof IPackageImport || element instanceof IRequiredBundle || element instanceof ILibraryImport;
+	}
+	
+	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java
new file mode 100644
index 0000000..0bb50d5
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class DirectoryHelper {
+	public static void scanBundles(AbstractBundleRepository repository, List<ISigilBundle> bundles, IPath path, IPath source, boolean recursive) {
+		File dir = path.toFile();
+		
+		if ( dir.exists() ) {
+			for ( File f : dir.listFiles() ){
+				if ( f.isDirectory() ) {
+					if ( recursive ) {
+						scanBundles( repository, bundles, new Path( f.getAbsolutePath() ), source, recursive );
+					}
+				}
+				else if ( f.isFile() && f.getName().endsWith( ".jar" )){
+					JarFile jar = null;
+					try {
+						jar = new JarFile(f);
+						ISigilBundle bundle = buildBundle(repository, jar.getManifest(), f );
+						if ( bundle != null ) {
+							bundle.setSourcePathLocation( source );
+							bundle.setSourceRootPath( new Path( "src" ) );
+							bundles.add( bundle );
+						}
+					} catch (IOException e) {
+						BldCore.error( "Failed to read jar file " + f, e );
+					} catch (ModelElementFactoryException e) {
+						BldCore.error( "Failed to build bundle " + f , e );
+					} catch (RuntimeException e) {
+						BldCore.error( "Failed to build bundle " + f , e );
+					}
+					finally {
+						if ( jar != null ) {
+							try {
+								jar.close();
+							} catch (IOException e) {
+								BldCore.error( "Failed to close jar file", e );
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private static ISigilBundle buildBundle(
+			AbstractBundleRepository repository, Manifest manifest, File f) {
+		IBundleModelElement info = repository.buildBundleModelElement( manifest );
+
+		ISigilBundle bundle = null;
+
+		if ( info != null ) {
+			bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+			bundle.addChild(info);
+			bundle.setLocation( new Path( f.getAbsolutePath() ) );
+		}
+
+		return bundle;
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java
new file mode 100644
index 0000000..3525a4e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+
+public class FileSystemRepository extends AbstractBundleRepository {
+
+	private ArrayList<ISigilBundle> bundles;
+	private IPath path;
+	private boolean recurse;
+	
+	public FileSystemRepository(String id, IPath path, boolean recurse) {
+		super(id);
+		this.path = path;
+		this.recurse = recurse;
+	}
+
+	@Override
+	public void accept(IRepositoryVisitor visitor, int options) {
+		synchronized( this ) {
+			if ( bundles == null ) {
+				bundles = new ArrayList<ISigilBundle>();
+				DirectoryHelper.scanBundles(this, bundles, path, null, recurse);
+			}
+		}
+		
+		for ( ISigilBundle b : bundles ) {
+			if ( !visitor.visit(b) ) {
+				break;
+			}
+		}		
+	}
+
+	public void refresh() {
+		synchronized( this ) {
+			bundles = null;
+		}
+		
+		notifyChange();
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java
new file mode 100644
index 0000000..093d06b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.Path;
+
+public class FileSystemRepositoryProvider implements IRepositoryProvider {
+
+	public IBundleRepository createRepository(String id, Properties preferences)
+			throws RepositoryException {
+		String dir = preferences.getProperty("dir");
+		if (!new File(dir).isDirectory()) {
+			throw new RepositoryException("directory '" + dir +"' does not exist.");
+		}
+		boolean recurse = Boolean.valueOf(preferences.getProperty("recurse"));
+		return new FileSystemRepository(id, new Path(dir), recurse);		
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java
new file mode 100644
index 0000000..5ca4ec3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+
+public class ProgressWrapper implements IProgressMonitor {
+
+	private IResolutionMonitor monitor;
+	
+	public ProgressWrapper(IResolutionMonitor monitor) {
+		this.monitor = monitor;
+	}
+
+	public boolean isCanceled() {
+		return monitor.isCanceled();
+	}
+
+	public void beginTask(String name, int totalWork) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void done() {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void internalWorked(double work) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setCanceled(boolean value) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setTaskName(String name) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void subTask(String name) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void worked(int work) {
+		// TODO Auto-generated method stub
+
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java
new file mode 100644
index 0000000..b23b64b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.util.jar.JarFile;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+
+public class SystemRepository extends AbstractBundleRepository {
+
+	private final String packages;
+	private final IPath frameworkPath;
+
+	public SystemRepository(String id, IPath frameworkPath, String packages) {
+		super(id);
+		this.frameworkPath = frameworkPath;
+		this.packages = packages;
+	}
+
+	private static ISigilBundle systemBundle;
+	
+	@Override
+	public void accept(IRepositoryVisitor visitor, int options) {
+		ISigilBundle bundle = loadSystemBundle();
+		
+		if ( bundle != null ) {
+			visitor.visit(bundle);
+		}
+	}
+
+	private synchronized ISigilBundle loadSystemBundle() {
+		if (systemBundle == null) {
+			systemBundle = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+			
+			JarFile jar = null;
+			
+			try {
+    			final IBundleModelElement info;
+				if (frameworkPath != null) {
+        			systemBundle.setLocation(frameworkPath);
+    				jar = new JarFile(frameworkPath.toFile());
+    				info = buildBundleModelElement(jar.getManifest());
+				} else {
+				    info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class);
+				}
+				
+    			applyProfile(info);
+    			systemBundle.addChild(info);
+			} catch (IOException e) {
+				BldCore.error( "Failed to read jar file " + frameworkPath, e );
+			} catch (ModelElementFactoryException e) {
+				BldCore.error( "Failed to build bundle " + frameworkPath , e );
+			} catch (RuntimeException e) {
+				BldCore.error( "Failed to build bundle " + frameworkPath , e );
+			}
+			finally {
+				if (jar != null) {
+					try {
+						jar.close();
+					} catch (IOException e) {
+						BldCore.error( "Failed to close jar file", e );
+					}
+				}
+			}
+		}
+		
+		return systemBundle;
+	}
+
+	private void applyProfile(IBundleModelElement info) {
+		if (packages != null) {
+			for (String name : packages.split(",\\s*")) {
+				IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+				pe.setPackageName(name);
+				info.addExport(pe);
+			}
+		}
+	}
+
+	public synchronized void refresh() {
+		systemBundle = null;
+		notifyChange();
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java
new file mode 100644
index 0000000..c13e278
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.Path;
+
+public class SystemRepositoryProvider implements IRepositoryProvider {
+
+	public IBundleRepository createRepository(String id, Properties properties)
+			throws RepositoryException {
+		String fw = properties.getProperty("framework");
+		Path frameworkPath = fw == null ? null : new Path(fw);
+		String extraPkgs = properties.getProperty("packages");
+		String profile = properties.getProperty("profile");
+		
+		try {
+			Properties p = readProfile(profile);
+    		String pkgs = p.getProperty("org.osgi.framework.system.packages") + "," + extraPkgs;
+			return new SystemRepository(id, frameworkPath, pkgs);
+		} catch (IOException e) {
+			throw new RepositoryException("Failed to load profile", e);
+		}
+	}
+	
+	public static Properties readProfile(String name) throws IOException {
+		if (name == null) {
+			String version = System.getProperty("java.specification.version");
+			String[] split = version.split("\\.");
+			String prefix = ("6".compareTo(split[1]) <= 0) ? "JavaSE-" : "J2SE-";
+			name = prefix + version;
+		}
+		
+		String profilePath = "profiles/" + name + ".profile";
+		InputStream in = SystemRepositoryProvider.class.getClassLoader().getResourceAsStream(profilePath);
+		
+		if (in == null)
+			throw new IOException("No such profile: " + profilePath);
+		
+		Properties p = new Properties();
+		p.load(in);
+		
+		return p;
+	}
+	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java
new file mode 100644
index 0000000..29c22d1
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.util;
+
+import java.util.ArrayList;
+
+public class QuoteUtil {
+	public static String[] split(String str) {
+		ArrayList<String> split = new ArrayList<String>();
+		boolean quote = false;
+		StringBuffer buf = new StringBuffer(str.length());
+		
+		for ( int i = 0; i < str.length(); i++ ) {
+			char c = str.charAt(i);
+			switch ( c ) {
+			case '"':
+				quote = !quote;
+				break;
+			case ',':
+				if ( !quote ) {
+					split.add( buf.toString().trim() );
+					buf.setLength(0);
+					break;
+				}
+				// else fall through on purpose
+			default:
+				buf.append( c );
+			}
+		}
+		
+		if ( buf.length() > 0 ) {
+			split.add( buf.toString().trim() );
+		}
+		return split.toArray( new String[split.size()] );
+	}	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java
new file mode 100644
index 0000000..98b1a22
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+public abstract class AbstractCompoundModelElement extends AbstractModelElement implements ICompoundModelElement {
+
+	private static final long serialVersionUID = 1L;
+	
+	public AbstractCompoundModelElement(String description) {
+		super(description);
+	}
+
+	public boolean addChild(IModelElement child) throws InvalidModelException {
+		return support.addChild(child);
+	}
+
+	public boolean removeChild(IModelElement child) {
+		return support.removeChild(child);
+	}
+
+	public IModelElement[] children() {
+		return support.children();
+	}
+	
+	private static final ThreadLocal<Map<IModelWalker,Set<IModelElement>>> walkedLocal = new ThreadLocal<Map<IModelWalker,Set<IModelElement>>>();
+	
+	public void visit(IModelWalker walker) {
+		if ( walker.visit( this ) ) {
+			Map<IModelWalker,Set<IModelElement>> walked = walkedLocal.get();
+			boolean delete = false;
+			
+			if ( walked == null ) {
+				walked = new HashMap<IModelWalker, Set<IModelElement>>();
+				walkedLocal.set(walked);
+			}
+
+			Set<IModelElement> check = walked.get(walker);
+			
+			if ( check == null ) {
+				delete = true;
+				check = new HashSet<IModelElement>(); 
+			}
+			
+			check.add( this );
+			
+			try {
+				for ( IModelElement e : children() ) {
+					if ( !check.contains( e ) && walker.visit( e ) ) {
+						check.add( e );
+						if ( e instanceof ICompoundModelElement ) {
+							ICompoundModelElement c = (ICompoundModelElement) e;
+							c.visit(walker);
+						}
+					}
+				}
+			}
+			finally {
+				if ( delete ) {
+					walked.remove(walker);
+					
+					if ( walked.isEmpty() ) {
+						walkedLocal.set( null );
+					}
+				}
+			}
+		}
+	}
+
+	public Set<Class<? extends IModelElement>> getOptionalChildren() {
+		return support.getChildrenTypes(false);
+	}
+
+	public Set<Class<? extends IModelElement>> getRequiredChildren() {
+		return support.getChildrenTypes(true);
+	}
+
+	public <T extends IModelElement> T[] childrenOfType(Class<T> type) {
+		return support.childrenOfType( type );
+	}
+
+
+	@Override
+	public void checkValid() throws InvalidModelException {
+		super.checkValid();
+		
+		for ( IModelElement e : support.children() ) {
+			e.checkValid();
+		}
+	}	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java
new file mode 100644
index 0000000..e65b3ea
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+public abstract class AbstractModelElement implements IModelElement {
+	
+	private static final long serialVersionUID = 1L;
+
+	private IModelElement parent;
+	
+    private String description;
+    private transient Map<Object, Object> meta;
+    private Map<Serializable, Serializable> serializedMeta;
+    private OverrideOptions override;
+    
+    protected final ModelElementSupport support;
+
+    public AbstractModelElement(String description) {
+    	support = new ModelElementSupport(this);
+    	this.description = description.intern();
+        this.meta = new HashMap<Object, Object>();
+        this.serializedMeta = new HashMap<Serializable, Serializable>();
+    }
+
+    public String getElementDescription() {
+        return description;
+    }
+
+    public Map<Object, Object> getMeta() {
+        return meta;
+    }
+
+    public void setMeta(Map<Object, Object> meta) {
+        this.meta = meta;
+    }
+
+    @Override
+    public AbstractModelElement clone() {
+		try {
+	        AbstractModelElement clone = (AbstractModelElement) super.clone();
+	   
+	        clone.meta = new HashMap<Object, Object>(meta);
+
+	        return clone;
+		} catch (CloneNotSupportedException e) {
+			// can't happen but make compiler happy
+			throw new IllegalStateException(e);
+		}
+    }
+
+    @SuppressWarnings("unchecked")
+	public <T extends IModelElement> T getAncestor(Class<T> type) {
+    	IModelElement parent = this.parent;
+    	
+    	while ( parent != null ) {
+    		if ( type.isInstance( parent ) ) {
+    			return (T) parent;
+    		}
+    		parent = parent.getParent();
+    	}
+    	
+		return null;
+	}
+
+	public IModelElement getParent() {
+		return parent;
+	}
+	
+	public void setParent( IModelElement parent ) {
+		if ( parent != null ) {
+			if ( this.parent != null && this.parent != parent ) {
+				throw new IllegalStateException( "Parent already installed");
+			}
+		}
+		
+		this.parent = parent;
+	}
+	
+    public void checkValid() throws InvalidModelException {
+    	for ( String req : getRequiredProperties() ) {
+    		try {
+				if ( getProperty( req ) == null ) {
+					throw new InvalidModelException(this, "Missing property " + req );
+				}
+			} catch (NoSuchMethodException e) {
+				throw new InvalidModelException( this, "No such property " + req );
+			}
+    	}
+	}
+
+	public Object getProperty(String name) throws NoSuchMethodException {
+		return support.getProperty(name);
+	}
+
+	public void setProperty(String name, Object value)
+			throws NoSuchMethodException {
+		support.setProperty(name, value);
+	}
+
+	public void addProperty(String name, Object value)
+			throws NoSuchMethodException {
+		support.addProperty(name, value);
+	}
+
+	public void removeProperty(String name, Object value)
+			throws NoSuchMethodException {
+		support.removeProperty(name, value);
+	}
+
+	public Object getDefaultPropertyValue(String name) {
+		return support.getDefaultPropertyValue( name );
+	}
+
+	public Set<String> getPropertyNames() {
+		return support.getPropertyNames();
+	}
+
+	public Set<String> getRequiredProperties() {
+		return Collections.emptySet();
+	}
+
+	protected Object writeReplace() {
+        AbstractModelElement clone = clone();
+
+        for (Map.Entry<Object, Object> e : clone.meta.entrySet()) {
+            if (e.getKey() instanceof Serializable && e.getValue() instanceof Serializable) {
+                serializedMeta.put((Serializable) e.getKey(), (Serializable) e.getValue());
+            }
+        }
+
+        clone.meta.clear();
+
+        return clone;
+    }
+
+    public Class<?> getPropertyType(String name) throws NoSuchMethodException {
+		return support.getPropertyType(name);
+	}
+
+	protected Object readResolve() {
+        this.meta = new HashMap<Object, Object>(serializedMeta);
+        serializedMeta.clear();
+        return this;
+    }
+
+	public OverrideOptions getOverride() {
+		return override;
+	}
+
+	public void setOverride(OverrideOptions override) {
+		this.override = override;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java
new file mode 100644
index 0000000..8df2b11
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.Set;
+
+public interface ICompoundModelElement extends IModelElement {
+    boolean addChild(IModelElement children) throws InvalidModelException;
+    
+    boolean removeChild(IModelElement children);
+    
+    IModelElement[] children();
+    
+    void visit(IModelWalker walker);
+    
+    <T extends IModelElement> T[] childrenOfType( Class<T> type );
+    
+    Set<Class<? extends IModelElement>> getRequiredChildren();
+    
+    Set<Class<? extends IModelElement>> getOptionalChildren();    
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java
new file mode 100644
index 0000000..0047bb9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependency extends IModelElement {
+	IDependent getDependent();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java
new file mode 100644
index 0000000..d97722e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependent extends IModelElement {
+	IDependency getDepender();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java
new file mode 100644
index 0000000..4b3c53e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependentModelElement extends IModelElement {
+    /**
+     * @return
+     */
+    IDependency[] getDependencies();
+    
+    /**
+     * @param dependency
+     */
+    void addDependency(IModelElement dependency);
+    
+    /**
+     * @param dependency
+     */
+    void removeDependency(IModelElement dependency);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java
new file mode 100644
index 0000000..4d230f0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Descriptors represent static information about component, composite or system. They allow other services to decide
+ * how to deal with the given entity without the need to directly interact with the entity.
+ * 
+ * @author dave
+ * 
+ */
+public interface IModelElement extends Cloneable {
+    /**
+     * A brief human readable description of the component, composite or system.
+     * 
+     * @return
+     */
+    String getElementDescription();
+    
+    /**
+     * A set of key value pairs designed for use by a machine to classify a particular component, composite or system.
+     * 
+     * @return
+     */
+    Map<Object, Object> getMeta();
+
+    /**
+     * Set meta data on this descriptor. Meta data is designed for use by a machine to classify or further enhance a
+     * particular component, composite or system.
+     * 
+     * @param meta
+     */
+    void setMeta(Map<Object, Object> meta);
+
+    /**
+     * Check to see if this descriptor defines a complete set of properties. The definition of what constitutes a
+     * complete set is up to the implementing class.
+     * 
+     * @throws InvalidModelException
+     */
+    void checkValid() throws InvalidModelException;
+    
+    /**
+     * Find the parent element of this model element or null if no parent exists.
+     * @return
+     */
+    IModelElement getParent();
+    
+    void setParent( IModelElement parent );
+        
+    /**
+     * Finds the first ancestor up the hierarch of parents which is an instance of the specified
+     * type.
+     * 
+     * @param type
+     * @return
+     */
+    <T extends IModelElement> T getAncestor(Class<T> type);
+    
+    IModelElement clone();
+    
+    Set<String> getPropertyNames();
+    
+    void setProperty(String name, Object value) throws NoSuchMethodException;
+    
+	void addProperty(String name, Object value) throws NoSuchMethodException;
+	
+	void removeProperty(String name, Object value) throws NoSuchMethodException;
+	
+    Object getProperty(String name) throws NoSuchMethodException;
+    
+    Object getDefaultPropertyValue(String name);
+    
+    Set<String> getRequiredProperties();
+
+	Class<?> getPropertyType(String name) throws NoSuchMethodException;
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java
new file mode 100644
index 0000000..b4525f8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IModelInfo {
+	String getGroupName();
+	String getGroupURI();
+	String getName();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java
new file mode 100644
index 0000000..a42c468
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+
+public interface IModelWalker {
+	boolean visit( IModelElement element );
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java
new file mode 100644
index 0000000..5a280db
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface INamedModelElement extends IModelElement {
+	void setName(String name);
+	String getName();
+    OverrideOptions getOverride();
+    void setOverride(OverrideOptions override);	
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java
new file mode 100644
index 0000000..9e9594c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IRequirementModelElement {
+	boolean accepts(IModelElement provider);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java
new file mode 100644
index 0000000..d4751c0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+/**
+ * @author dave
+ * 
+ */
+public class InvalidModelException extends RuntimeException {
+	
+	private static final long serialVersionUID = 1L;
+	
+	private IModelElement target;
+	
+    public InvalidModelException(IModelElement target, String msg) {
+        super(msg);
+        this.target = target;
+    }
+
+    public InvalidModelException(IModelElement target, String msg, Throwable t) {
+        super(msg, t);
+        this.target = target;
+    }
+    
+    public IModelElement getTarget() {
+    	return target;
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java
new file mode 100644
index 0000000..186d2d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ModelElementFactory {
+	static class ElementInfo {
+		Class<? extends IModelElement> implType;
+		String name;
+		String groupName;
+		String groupURI;
+		
+		public ElementInfo(Class<? extends IModelElement> implType, String name, String groupName, String groupURI) {
+			this.implType = implType;
+			this.name = name;
+			this.groupName = groupName;
+			this.groupURI = groupURI;
+		}
+		
+		public Class<? extends IModelElement> getImplType() {
+			return implType;
+		}
+		public String getName() {
+			return name;
+		}
+		
+		public String getGroupName() {
+			return groupName;
+		}
+
+		public String getGroupURI() {
+			return groupURI;
+		}
+		
+		public String toString() {
+			return "ElementInfo[" + name + ":" + groupName + ":" + groupURI + ":" + implType.getCanonicalName() + "]";
+		}
+	}
+
+	static class ModelInfo implements IModelInfo {
+
+		private ElementInfo e;
+		
+		public ModelInfo(ElementInfo e) {
+			this.e = e;
+		}
+
+		public String getGroupName() {
+			return e.getGroupName();
+		}
+
+		public String getGroupURI() {
+			return e.getGroupURI();
+		}
+
+		public String getName() {
+			return e.getName();
+		}
+
+	}
+
+	static class DefaultModelElementFactory extends ModelElementFactory {
+		private HashMap<Class<? extends IModelElement>, ElementInfo> elementInfo = new HashMap<Class<? extends IModelElement>, ElementInfo>();
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T extends IModelElement> T newModelElement( Class<T> type ) throws ModelElementFactoryException {
+			ElementInfo info = elementInfo.get( type );
+			if ( info == null ) {
+				throw new ModelElementFactoryException( "No implementation registered for " + type );
+			}
+			try {
+				return (T) info.getImplType().newInstance();
+			} catch (InstantiationException e) {
+				throw new ModelElementFactoryException(e);
+			} catch (IllegalAccessException e) {
+				throw new ModelElementFactoryException(e);
+			}
+		}
+
+		@Override
+		public <T extends IModelElement> void register(Class<T> type, Class<? extends T> impl, String name, String groupName, String groupURI ) {
+			elementInfo.put( type, new ElementInfo( impl, name, groupName, groupURI ) );
+		}
+
+		@Override
+		public <T extends IModelElement> void unregister(Class<T> type,
+				Class<? extends T> impl) {
+			ElementInfo info = elementInfo.get( type );
+			if ( info != null && info.getImplType() == impl ) {
+				elementInfo.remove(type);
+			}
+		}
+
+		@Override
+		public IModelInfo getModelInfo(Class<? extends IModelElement> type) {
+			ElementInfo e = findElementInfo( type );
+			
+			if ( e == null ) {
+				return null;
+			}
+			
+			return new ModelInfo( e );
+		}
+
+		@Override
+		public IModelElement newModelElement(String namespaceURI, String localPart) throws ModelElementFactoryException {
+			for ( Map.Entry<Class<? extends IModelElement>, ElementInfo> e : elementInfo.entrySet() ) {
+				ElementInfo i = e.getValue();
+				if ( equal( namespaceURI, i.getGroupURI() ) && equal( i.getName(), localPart ) ) {
+					return newModelElement(e.getKey());
+				}
+			}
+			
+			return null;
+		}
+
+		private boolean equal(String val1, String val2) {
+			return val1 == null ? val2 == null : val1.equals( val2 );
+		}
+
+		private ElementInfo findElementInfo( Class<? extends IModelElement> type ) {
+			for ( ElementInfo e : elementInfo.values() ) {
+				if ( e.getImplType() == type ) {
+					return e;
+				}
+			}
+			
+			return null;
+		}
+
+	}
+
+	public abstract <T extends IModelElement> T newModelElement( Class<T> type ) throws ModelElementFactoryException;
+	
+	public abstract IModelElement newModelElement(String namespaceURI, String localPart) throws ModelElementFactoryException;	
+	
+	public abstract <T extends IModelElement> void register( Class<T> type, Class<? extends T> impl, String name, String groupName, String groupURI );
+	
+	public abstract <T extends IModelElement> void unregister( Class<T> type, Class<? extends T> impl );
+
+	public abstract IModelInfo getModelInfo( Class<? extends IModelElement> type );
+	
+	private static ModelElementFactory instance = new DefaultModelElementFactory();
+
+	public static ModelElementFactory getInstance() {
+		return instance;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java
new file mode 100644
index 0000000..c450537
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public class ModelElementFactoryException extends RuntimeException {
+
+	private static final long serialVersionUID = 1L;
+
+	public ModelElementFactoryException(Throwable t) {
+		super(t);
+	}
+
+	public ModelElementFactoryException(String msg) {
+		super(msg);
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java
new file mode 100644
index 0000000..40c64f0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java
@@ -0,0 +1,728 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.io.Serializable;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Logger;
+
+
+public class ModelElementSupport implements Serializable {
+
+	private static final Logger log = Logger.getLogger( ModelElementSupport.class.getName() );
+	
+	private static final long serialVersionUID = 1L;
+	
+	private static final PropertyAdapter[] EMPTY_PROPS = new PropertyAdapter[] {};
+	private static final IModelElement[] EMPTY_ELEMENTS = new IModelElement[] {};
+	private static final Object[] ZERO_ARGS = new Object[] {};
+	private static final Class<?>[] ZERO_PARAMS = new Class[] {};
+	
+	private static final WeakHashMap<Class<?>, SoftReference<ChildAdapter[]>> adapterCache = new WeakHashMap<Class<?>, SoftReference<ChildAdapter[]>>();;	
+	private static final WeakHashMap<Class<?>, SoftReference<PropertyAdapter[]>> propertyCache = new WeakHashMap<Class<?>, SoftReference<PropertyAdapter[]>>();;	
+	
+	private IModelElement target;
+	
+	private transient SoftReference<PropertyAdapter[]> propertyReference;
+	private transient SoftReference<ChildAdapter[]> childrenReference;
+	private transient SoftReference<Set<String>> propertyNameReference;
+	
+	public ModelElementSupport(IModelElement target) {
+		this.target = target;
+	}
+
+	public void setProperty(String name, Object value) throws NoSuchMethodException {
+		PropertyAdapter p = findProperty( name, value );
+		if ( p == null ) {
+			throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+		}
+		invoke( target, p.getWriteMethod(), value );
+	}
+	
+	public void addProperty(String name, Object value) throws NoSuchMethodException {
+		PropertyAdapter p = findProperty( name, value );
+		if ( p == null ) {
+			throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+		}
+		invoke( target, p.getAddMethod(), value );
+	}
+
+	public void removeProperty(String name, Object value) throws NoSuchMethodException {
+		PropertyAdapter p = findProperty( name, value );
+		if ( p == null ) {
+			throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+		}
+		invoke( target, p.getRemoveMethod(), value );
+	}
+	
+	public Object getProperty( String name ) throws NoSuchMethodException {
+		PropertyAdapter p = findProperty( name, null );
+		if ( p == null ) {
+			throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+		}
+		return invoke( target, p.getReadMethod(), ZERO_ARGS );
+	}
+	
+	public Set<String> getPropertyNames() {
+		Set<String> names = propertyNameReference == null ? null : propertyNameReference.get();
+		
+		if ( names == null ) {
+			names = new HashSet<String>();
+			
+			PropertyAdapter[] props = cachedProps( target.getClass() );
+			for ( PropertyAdapter prop : props ) {
+				names.add( prop.getName() );
+			}
+			
+			propertyNameReference = new SoftReference<Set<String>>(names);
+		}
+		
+		return names;
+	}
+	
+	public Object getDefaultPropertyValue(String name) {
+		try {
+			Method m = target.getClass().getMethod( makeDefaultPropertyValue( name ), ZERO_PARAMS );
+			return invoke( target, m, ZERO_ARGS );
+		} catch (SecurityException e) {
+			throw new UndeclaredThrowableException(e);
+		} catch (NoSuchMethodException e) {
+			// fine no default
+			return null;
+		}
+	}	
+	
+	public Class<?> getPropertyType(String name) throws NoSuchMethodException {
+		PropertyAdapter p = findProperty( name, null );
+		if ( p == null ) {
+			throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+		}
+		return p.getPropertyType();
+	}	
+	
+	@SuppressWarnings("unchecked")
+	public <T extends IModelElement> T[] childrenOfType( Class<T> type ) {
+		ChildAdapter[] adapters = cachedAdapters();
+		
+		if ( adapters.length == 0 ) {
+			// return (T[]) EMPTY_ELEMENTS;
+    		return ((T[])Array.newInstance(type, 0));
+		}
+		
+		ArrayList<T> elements = new ArrayList<T>();
+		
+		for ( ChildAdapter adapter : adapters ) {
+			Collection<? extends IModelElement> val = adapter.members(target);
+			
+			for ( IModelElement e : val ) {
+				if ( type.isInstance(e) ) {
+					elements.add( (T) e );
+				}
+			}
+		}
+		
+		//return elements.toArray( (T[]) EMPTY_ELEMENTS );
+		return elements.toArray((T[])Array.newInstance(type, elements.size()));
+	}
+	
+	public IModelElement[] children() {
+		ChildAdapter[] adapters = cachedAdapters();
+		
+		if ( adapters.length == 0 ) {
+			return EMPTY_ELEMENTS;
+		}
+		
+		ArrayList<IModelElement> elements = new ArrayList<IModelElement>();
+		
+		for ( ChildAdapter adapter : adapters ) {
+			elements.addAll( adapter.members(target) );
+		}
+		
+		return elements.toArray( EMPTY_ELEMENTS );
+	}
+	
+	public boolean addChild(IModelElement element) throws InvalidModelException {
+		if ( element.getParent() == null ) {
+			ChildAdapter[] adapters = cachedAdapters();
+			
+			if ( adapters.length > 0 ) {
+				for ( ChildAdapter adapter : adapters ) {
+					if ( adapter.add( target, element ) ) {
+						element.setParent(target);
+						return true;
+					}
+				}
+			}
+		}
+		
+		return false;
+	}
+	
+	public boolean removeChild(IModelElement element) {
+		if ( element.getParent() == target ) {
+			ChildAdapter[] adapters = cachedAdapters();
+			
+			if ( adapters.length > 0 ) {
+				for ( ChildAdapter adapter : adapters ) {
+					if ( adapter.remove( target, element ) ) {
+						element.setParent( null );
+						return true;
+					}
+				}
+			}
+		}
+		
+		return false;
+	}
+
+	public Set<Class<? extends IModelElement>> getChildrenTypes(boolean required) {
+		ChildAdapter[] adapters = cachedAdapters();
+		
+		if ( adapters.length == 0 ) {
+			return Collections.emptySet();
+		}
+		
+		HashSet<Class<? extends IModelElement>> types = new HashSet<Class<? extends IModelElement>>();
+		
+		for ( ChildAdapter adapter : adapters ) {
+			if ( adapter.isRequired() == required ) {
+				Class<? extends IModelElement> type = adapter.getType();
+				
+				if ( type != null ) {
+					types.add( type );
+				}
+			}
+		}
+		
+		return types;
+	}
+	
+	private PropertyAdapter findProperty(String name, Object value) {
+		PropertyAdapter[] props = propertyReference == null ? null : propertyReference.get();
+		
+		if ( props == null ) {
+			props = cachedProps( target.getClass() );
+			propertyReference = new SoftReference<PropertyAdapter[]>( props );
+		}
+		
+		for ( PropertyAdapter prop : props ) {
+			if ( prop.getName().equals( name ) && (value == null || prop.getRawType().isAssignableFrom(value.getClass() ) ) ) {
+				return prop;
+			}
+		}
+		
+		return null;
+	}
+
+	private static PropertyAdapter[] cachedProps(
+			Class<? extends IModelElement> type) {
+		SoftReference<PropertyAdapter[]> ref = propertyCache.get( type );
+		
+		PropertyAdapter[] props = ref == null ? null : ref.get();
+		
+		if ( props == null ) {
+			props = loadProps( type );
+			propertyCache.put( type, new SoftReference<PropertyAdapter[]>( props ) );
+		}
+		
+		return props;
+	}
+
+	private static PropertyAdapter[] loadProps(Class<? extends IModelElement> type) {
+		ArrayList<PropertyAdapter> props = new ArrayList<PropertyAdapter>();
+		for ( Method m : type.getMethods() ) {
+			if ( isValidProperty( m )) {
+				try {
+					PropertyAdapter p = new PropertyAdapter( m, type );
+					props.add( p );
+				} catch (NoSuchMethodException e) {
+					// fine not a bean method
+					log.finer( "Invalid bean property method " + m + ": " + e.getMessage() );
+				}
+			}
+		}
+		
+		return props.toArray( EMPTY_PROPS );
+	}
+
+	private static boolean isValidProperty(Method m) {
+		return m.getName().startsWith( "get" ) && m.getParameterTypes().length == 0 && !m.getDeclaringClass().equals( Object.class ) && !IModelElement.class.isAssignableFrom(m.getReturnType());
+	}
+
+	private static String makeDefaultPropertyValue(String name) {
+		return "getDefault" + capitalise( name );
+	}
+
+	private static String capitalise(String name) {
+		return Character.toUpperCase(name.charAt(0))  + name.substring(1);
+	}
+
+	private static String decapitalise(String substring) {
+		return Character.toLowerCase(substring.charAt(0)) + substring.substring(1);
+	}
+
+	private ChildAdapter[] cachedAdapters() {
+		ChildAdapter[] adapters = childrenReference == null ? null : childrenReference.get();
+		
+		if ( adapters == null ) {
+			adapters = loadAdapters( target );
+			childrenReference = new SoftReference<ChildAdapter[]>( adapters );
+		}		
+		
+		return adapters;
+	}
+	
+	private static ChildAdapter[] loadAdapters(IModelElement target) {
+		Class<? extends IModelElement> type = target.getClass();
+		SoftReference<ChildAdapter[]> ref = adapterCache.get( type );
+		
+		ChildAdapter[] adapters = ref == null ? null : ref.get();
+		
+		if ( adapters == null ) {
+			adapters = buildAdapters( type );
+			adapterCache.put( type, new SoftReference<ChildAdapter[]>( adapters ) );
+		}
+		
+		return adapters;
+	}
+	
+	private static ChildAdapter[] buildAdapters(Class<? extends IModelElement> type) {
+		ArrayList<ChildAdapter> adapters = new ArrayList<ChildAdapter>();
+		
+		for ( Method m : type.getMethods() ) {
+			ChildAdapter adapter = null;
+			
+			if ( isValidGetProperty( m ) ) {
+				adapter = buildGetAdapter( m );
+			}
+			else if ( isValidSetProperty( m ) ) {
+				adapter = buildSetAdapter( m );
+			}
+			else if ( isValidAddProperty( m ) ) {
+				adapter = buildAddAdapter( m );
+			}
+			else if ( isValidRemoveProperty( m ) ) {
+				adapter = buildRemoveAdapter( m );
+			}
+			
+			if ( adapter != null ) {
+				adapters.add( adapter );
+			}
+		}
+		
+		return adapters.toArray( new ChildAdapter[adapters.size()] );
+	}
+
+	private static ChildAdapter buildGetAdapter(Method m) {
+		if ( IModelElement.class.isAssignableFrom( m.getReturnType() ) ) {
+			return new GetPropertyAdapter( m );
+		}
+		else if ( Collection.class.isAssignableFrom( m.getReturnType() ) ) {
+			return new GetCollectionAdapter( m );
+		}
+		else if ( isModelArray( m.getReturnType() ) ) {
+			return new GetArrayAdapter( m );
+		}
+		else {
+			return null;
+		}
+	}
+	
+	private static ChildAdapter buildSetAdapter(Method m) {
+		if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+			return new SetPropertyAdapter( m );
+		}
+		else {
+			return null;
+		}
+	}
+
+	private static ChildAdapter buildAddAdapter(Method m) {
+		if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+			return new AddPropertyAdapter( m );
+		}
+		else {
+			return null;
+		}
+	}
+
+	private static ChildAdapter buildRemoveAdapter(Method m) {
+		if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+			return new RemovePropertyAdapter( m );
+		}
+		else {
+			return null;
+		}
+	}
+
+	private static boolean isValidRemoveProperty(Method m) {
+		return m.getParameterTypes().length == 1 && m.getName().startsWith( "remove" ) && !isDeclared( ICompoundModelElement.class, m );
+	}
+
+	private static boolean isValidAddProperty(Method m) {
+		return m.getParameterTypes().length == 1 && m.getName().startsWith( "add" ) && !isDeclared( ICompoundModelElement.class, m );
+	}
+
+	private static boolean isDeclared(Class<? extends IModelElement> element, Method m) {
+		try {
+			element.getMethod( m.getName(), m.getParameterTypes() );
+			return true;
+		} catch (SecurityException e) {
+			throw new UndeclaredThrowableException( e );
+		} catch (NoSuchMethodException e) {
+			return false;
+		}
+	}
+
+	private static boolean isValidSetProperty(Method m) {
+		return m.getParameterTypes().length == 1 && m.getName().startsWith( "set" ) && !isDeclared( IModelElement.class, m );
+	}
+
+	private static boolean isValidGetProperty(Method m) {
+		return m.getParameterTypes().length == 0 && m.getName().startsWith( "get" ) && !isDeclared( IModelElement.class, m ) && !isDeclared(ICompoundModelElement.class, m);
+	}
+
+	private static Object invoke( Object target, Method m, Object... args ) {
+		try {
+			return m.invoke(target, args);
+		} catch (IllegalArgumentException e) {
+			// this should already have been tested
+			throw new IllegalStateException(e);
+		} catch (IllegalAccessException e) {
+			throw new UndeclaredThrowableException( e );
+		} catch (InvocationTargetException e) {
+			throw new UndeclaredThrowableException( e.getCause() );
+		}
+	}		
+
+	private static class PropertyAdapter {
+
+		String prop;
+		String name;
+		Method g;
+		Method s;
+		Method a;
+		Method r;
+		Class<?> propertyType;
+		
+		public PropertyAdapter(Method g, Class<?> type) throws SecurityException, NoSuchMethodException {
+			if ( g.getReturnType().isArray() || Iterable.class.isAssignableFrom(g.getReturnType() ) ) {
+				prop = g.getName().substring(3);
+				// remove trailing s - as in addWibble, removeWibble, getWibbles
+				prop = prop.substring(0, prop.length() - 1);
+				name = decapitalise( prop );
+				a = find( "add", prop, g.getReturnType(), type );
+				propertyType = a.getParameterTypes()[0];
+				r = find( "remove", prop, g.getReturnType(), type );
+				if ( r.getParameterTypes()[0] != propertyType ) {
+					throw new NoSuchMethodException( "Add remove property method types do not match" );
+				}
+				propertyType = Array.newInstance(propertyType, 0).getClass();
+			}
+			else {
+				prop = g.getName().substring(3);
+				name = decapitalise( prop );
+				propertyType = g.getReturnType();
+				s = find( "set", prop, propertyType, type );
+			}
+			
+			this.g = g;
+		}
+		
+		public Class<?> getRawType() {
+			return propertyType.isArray() ? propertyType.getComponentType() : propertyType;
+		}
+
+		public Class<?> getPropertyType() {
+			return propertyType;
+		}
+
+		public Method getReadMethod() {
+			return g;
+		}
+
+		public Method getAddMethod() throws NoSuchMethodException {
+			if ( a == null ) {
+				throw new NoSuchMethodException( "No such method add" + prop ); 
+			}
+			
+			return a;
+		}
+
+		public Method getRemoveMethod() throws NoSuchMethodException {
+			if ( r == null ) {
+				throw new NoSuchMethodException( "No such method remove" + prop ); 
+			}
+			
+			return r;
+		}
+
+		public Method getWriteMethod() throws NoSuchMethodException {
+			if ( s == null ) {
+				throw new NoSuchMethodException( "No such method set" + prop ); 
+			}
+			
+			return s;
+		}
+		
+		@Override
+		public String toString() {
+			return "PropertyAdapter[" + name + "]";
+		}
+
+		private Method find(String prefix, String prop, Class<?> returnType, Class<?> type) throws SecurityException, NoSuchMethodException {
+			String methodName = prefix + prop;
+			
+			if ( returnType.isArray() ) {
+				Class<?> t = returnType.getComponentType();
+				return type.getMethod( methodName, new Class[] { t } );
+			}
+			else if ( Iterable.class.isAssignableFrom( returnType ) ) {
+				Method f = null;
+				for ( Method m : type.getMethods() ) {
+					if ( m.getParameterTypes().length == 1 && m.getName().equals( methodName ) && !IModelElement.class.isAssignableFrom(m.getParameterTypes()[0]) ) {
+						if ( f == null ) {
+							f = m;
+						}
+						else {
+							throw new NoSuchMethodException( "Found duplicate " + methodName );
+						}
+					}
+				}
+				if ( f == null ) {
+					throw new NoSuchMethodException( "No such method " + methodName );
+				}
+				
+				return f;
+			}
+			else {
+				return type.getMethod( methodName, new Class[] { returnType } ); 
+			}
+		}
+		public String getName() {
+			return name;
+		}
+		
+	}
+	
+	private static abstract class ChildAdapter {
+		Method m;
+		
+		ChildAdapter( Method m ) {
+			this.m = m;
+		}
+		
+		public boolean isRequired() {
+			return m.isAnnotationPresent(Required.class);
+		}
+
+		boolean add(Object target, IModelElement element) {
+			return false;
+		}
+
+		abstract Class<? extends IModelElement> getType();
+
+		boolean remove(Object target, IModelElement element) {
+			return false;
+		}
+
+		Collection<? extends IModelElement> members(Object target) {
+			return Collections.emptyList();
+		}
+
+		@Override
+		public String toString() {
+			return "ChildAdapter[ " + m.getName() + "]";
+		}	
+	}
+	
+	private static class GetPropertyAdapter extends ChildAdapter {
+		GetPropertyAdapter(Method m) {
+			super(m);
+		}
+
+		@Override
+		Collection<? extends IModelElement> members(Object target) {
+			IModelElement member = (IModelElement) invoke( target, m, ZERO_ARGS );
+			if ( member == null ) {
+				return Collections.emptyList();
+			}
+			else {
+				return Collections.<IModelElement>singleton( member );
+			}
+		}
+
+		@SuppressWarnings("unchecked")
+		@Override
+		Class<? extends IModelElement> getType() {
+			return (Class<? extends IModelElement>) m.getReturnType();
+		}
+	}	
+	
+	private static class GetCollectionAdapter extends ChildAdapter {
+		public GetCollectionAdapter(Method m) {
+			super(m);
+		}
+
+		@SuppressWarnings("unchecked")
+		@Override
+		Collection<? extends IModelElement> members(Object target) {
+			Collection members = (Collection) invoke( target, m, ZERO_ARGS );
+			if ( members == null ) {
+				return Collections.emptyList();
+			}
+			else {
+				ArrayList<IModelElement> safe = new ArrayList<IModelElement>(members.size());
+				for ( Object o : members ) {
+					if ( o instanceof IModelElement ) {
+						safe.add( (IModelElement) o );
+					}
+				}
+				return safe;
+			}
+		}
+		
+		@Override
+		Class<? extends IModelElement> getType() {
+			// impossible to get type of a collection as erasure removes generics info
+			return null;
+		}		
+		
+	}
+	
+	private static class GetArrayAdapter extends ChildAdapter {
+		public GetArrayAdapter(Method m) {
+			super(m);
+		}
+
+		@Override
+		Collection<? extends IModelElement> members(Object target) {
+			IModelElement[] array = (IModelElement[]) invoke( target, m, ZERO_ARGS );
+			if ( array == null || array.length == 0) {
+				return Collections.emptyList();
+			}
+			else {
+				return (Collection<? extends IModelElement>) Arrays.asList( array );
+			}
+		}
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		Class<? extends IModelElement> getType() {
+			return (Class<? extends IModelElement>) m.getReturnType().getComponentType();
+		}
+	}
+	
+	private static class SetPropertyAdapter extends ChildAdapter {
+		public SetPropertyAdapter(Method m) {
+			super(m);
+		}
+
+		@Override
+		boolean add(Object target, IModelElement element) {
+			if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+				invoke(target, m, new Object[] { element } );
+				return true;
+			}
+			else {
+				return false;
+			}
+		}
+
+		@Override
+		boolean remove(Object target, IModelElement element) {
+			if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+				invoke(target, m, new Object[] { null } );
+				return true;
+			}
+			else {
+				return false;
+			}
+		}		
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		Class<? extends IModelElement> getType() {
+			return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+		}		
+	}	
+	
+	private static class AddPropertyAdapter extends ChildAdapter {
+		public AddPropertyAdapter(Method m) {
+			super(m);
+		}
+
+		@Override
+		boolean add(Object target, IModelElement element) {
+			if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+				invoke(target, m, new Object[] { element } );
+				return true;
+			}
+			else {
+				return false;
+			}
+		}
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		Class<? extends IModelElement> getType() {
+			return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+		}		
+	}
+	
+	private static class RemovePropertyAdapter extends ChildAdapter {
+
+		public RemovePropertyAdapter(Method m) {
+			super(m);
+		}
+
+		@Override
+		boolean remove(Object target, IModelElement element) {
+			if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+				invoke(target, m, new Object[] { element } );
+				return true;
+			}
+			else {
+				return false;
+			}
+		}
+		
+		@SuppressWarnings("unchecked")
+		@Override
+		Class<? extends IModelElement> getType() {
+			return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+		}		
+	}
+	
+	private static boolean isModelArray(Class<?> returnType) {
+		return returnType.isArray() && IModelElement.class.isAssignableFrom(returnType.getComponentType() );
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java
new file mode 100644
index 0000000..c82fea7
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author dave
+ * 
+ */
+public enum OverrideOptions {
+    NO("no"), MAY("may"), MUST("must");
+
+    private String str;
+
+    private static Map<String, OverrideOptions> map = new HashMap<String, OverrideOptions>();
+
+    static {
+        for (OverrideOptions option : OverrideOptions.values()) {
+            map.put(option.str.toLowerCase(), option);
+        }
+    }
+
+    private OverrideOptions(String str) {
+        this.str = str;
+    }
+
+    public static OverrideOptions parse(String val) {
+        OverrideOptions option = map.get(val.toLowerCase());
+
+        if (option == null) {
+            throw new IllegalArgumentException("Invalid override value " + val);
+        }
+
+        return option;
+    }
+    
+    @Override
+    public String toString() {
+        return str;
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java
new file mode 100644
index 0000000..79b4278
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Required {
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java
new file mode 100644
index 0000000..28b83d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class And implements LDAPExpr {
+
+    /**
+     */
+    private static final long serialVersionUID = 1L;
+    private LDAPExpr[] children;
+
+    public static LDAPExpr apply(LDAPExpr... terms) {
+        if (terms == null) {
+            throw new NullPointerException("terms cannot be null");
+        }
+        else if (terms.length == 0) {
+            return Expressions.T;
+        }
+        else if (terms.length == 1) {
+            return terms[0];
+        }
+        LDAPExpr[] filtered = new LDAPExpr[terms.length];
+        int ctr = 0;
+        for (int i = 0; i < terms.length; i++) {
+            if (terms[i].equals(Expressions.F))
+                return Expressions.F;
+            if (terms[i].equals(Expressions.T))
+                continue;
+            filtered[ctr] = terms[i];
+            ctr++;
+        }
+        if (ctr == 0) {
+            return Expressions.T;
+        }
+        else if (ctr == 1) {
+            return filtered[0];
+        }
+        LDAPExpr[] andTerms = new LDAPExpr[ctr];
+        System.arraycopy(filtered, 0, andTerms, 0, ctr);
+
+        return new And(andTerms);
+    }
+
+    private And(LDAPExpr... children) {
+        this.children = children;
+    }
+
+    public boolean eval(Map<String, ?> map) {
+        for (int i = 0; i < children.length; i++) {
+            if (!children[i].eval(map)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public void visit(ExprVisitor v) {
+        v.visitAnd(this);
+    }
+
+    public LDAPExpr[] getChildren() {
+        return children;
+    }
+
+    public void setChildren(LDAPExpr[] children) {
+        this.children = children;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof And) {
+            And that = (And) other;
+            if (children.length != that.children.length) {
+                return false;
+            }
+            for (int i = 0; i < children.length; i++) {
+                if (!children[i].equals(that.children[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer(256);
+        buf.append("(&");
+        for (int i = 0; i < children.length; i++) {
+            buf.append(" ").append(children[i]).append(" ");
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java
new file mode 100644
index 0000000..ecf105e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+/**
+ * Immutable class representing cardinality constraints between two entities.
+ * 
+ */
+public class Cardinality implements Serializable {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    public static final Cardinality ZERO_TO_MANY = new Cardinality(0, -1);
+    public static final Cardinality ONE_TO_MANY = new Cardinality(1, -1);
+    public static final Cardinality ZERO_TO_ONE = new Cardinality(0, 1);
+    public static final Cardinality ONE_TO_ONE = new Cardinality(1, 1);
+
+    private int min;
+    private int max;
+
+    /**
+     * @param min
+     *            >=0 (usually 0 or 1)
+     * @param max
+     *            >=min or -1 to indicate an unbounded maximum
+     */
+    public Cardinality(int min, int max) {
+        if (min < 0) {
+            throw new IllegalArgumentException("Min cannot be less than 0");
+        }
+
+        if ((max < min) && (max != -1)) {
+            throw new IllegalArgumentException("Max cannot be less than min");
+        }
+
+        this.min = min;
+        this.max = max;
+    }
+
+    public int getMin() {
+        return min;
+    }
+
+    public int getMax() {
+        return max;
+    }
+
+    public String toString() {
+        return min + ".." + ((max == -1) ? ("n") : (Integer.toString(max)));
+    }
+
+    public boolean isDefined(Cardinality cardinality) {
+        return (min <= cardinality.min) && ((max == -1) || (max >= cardinality.max));
+    }
+
+    public boolean isSingleton() {
+        return (min == 1) && (max == 1);
+    }
+
+    public static Cardinality parse(String stringRep) throws IllegalArgumentException {
+        stringRep = stringRep.trim();
+
+        int dotdot = stringRep.indexOf("..");
+
+        if (dotdot == -1) {
+            throw new IllegalArgumentException("Invalid cardinality string representation, expected ..");
+        }
+
+        String minStr = stringRep.substring(0, dotdot);
+        String maxStr = stringRep.substring(dotdot + 2);
+
+        int min = Integer.parseInt(minStr);
+        int max = min;
+
+        if ("n".equals(maxStr)) {
+            max = -1;
+        }
+        else {
+            max = Integer.parseInt(maxStr);
+        }
+
+        return cardinality(min, max);
+    }
+
+    public static Cardinality cardinality(int min, int max) {
+        Cardinality c = null;
+
+        if (min == 0) {
+            if (max == 1) {
+                c = ZERO_TO_ONE;
+            }
+            else if (max == -1) {
+                c = ZERO_TO_MANY;
+            }
+        }
+        else if (min == 1) {
+            if (max == 1) {
+                c = ONE_TO_ONE;
+            }
+            else if (max == -1) {
+                c = ONE_TO_MANY;
+            }
+        }
+
+        if (c == null)
+            c = new Cardinality(min, max);
+
+        return c;
+    }
+
+    public int hashCode() {
+        return max ^ min;
+    }
+
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        try {
+            Cardinality c = (Cardinality) o;
+
+            return (min == c.min) && (max == c.max);
+        }
+        catch (ClassCastException cce) {
+            return false;
+        }
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java
new file mode 100644
index 0000000..e112928
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public interface ExprVisitor {
+
+    void visitAnd(And a);
+    void visitOr(Or o);
+    void visitNot(Not n);
+    void visitSimple(SimpleTerm st);
+    // if none of the above matches use this
+    void visitAny(LDAPExpr ex);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java
new file mode 100644
index 0000000..24ea698
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Expressions {
+
+    public static LDAPExpr and(LDAPExpr... terms) {
+        return And.apply(terms);
+    }
+
+    public static LDAPExpr or(LDAPExpr... terms) {
+        return Or.apply(terms);
+    }
+
+    public static LDAPExpr not(LDAPExpr e) {
+        return Not.apply(e);
+    }
+
+    public static LDAPExpr T = Bool.TRUE;
+    public static LDAPExpr F = Bool.FALSE;
+
+    // supports direct use of wildcards for ease of testing, but not literal *s
+    public static SimpleTerm ex(String name, Ops op, String rhs) {
+
+        rhs = rhs.replace('*', SimpleTerm.WILDCARD);
+        return new SimpleTerm(name, op, rhs);
+    }
+
+}
+
+class Bool implements LDAPExpr {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    public static final Bool TRUE = new Bool(true);
+    public static final Bool FALSE = new Bool(false);
+
+    private boolean bool;
+
+    public Bool(boolean bool) {
+        this.bool = bool;
+    }
+
+    public boolean eval(Map<String, ?> map) {
+        return bool;
+    }
+
+    public void visit(ExprVisitor v) {
+    }
+
+    public LDAPExpr[] getChildren() {
+        return CHILDLESS;
+    }
+
+    public String toString() {
+        return "(" + bool + ")";
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java
new file mode 100644
index 0000000..8e3ebad
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public interface FilterValidator {
+
+    public static FilterValidator ACCEPT_ALL = new AcceptEverythingValidator();
+
+    boolean validate(LDAPExpr filter);
+
+    static class AcceptEverythingValidator implements FilterValidator {
+
+        public boolean validate(LDAPExpr filter) {
+            return true;
+        }
+
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java
new file mode 100644
index 0000000..a009b76
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public interface LDAPExpr extends Serializable {
+
+    public static final LDAPExpr[] CHILDLESS = new LDAPExpr[] {};
+    public static LDAPExpr ACCEPT_ALL = Expressions.T;
+
+    LDAPExpr[] getChildren();
+
+    void visit(ExprVisitor v);
+
+    boolean equals(Object other);
+
+    int hashCode();
+
+    boolean eval(Map<String, ?> map);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java
new file mode 100644
index 0000000..2e67024
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public class LDAPParseException extends Exception {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    private ParseState ps;
+    private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\\r\\n");
+
+    public LDAPParseException(String message, ParseState ps) {
+        super(message);
+        this.ps = ps;
+    }
+
+    public LDAPParseException(String message) {
+        super(message);
+    }
+
+    @Override
+    public String getMessage() {
+        if (ps == null) {
+            return super.getMessage();
+        }
+
+        String basicMessage = super.getMessage();
+        StringBuffer buf = new StringBuffer(basicMessage.length() + ps.str.length() * 2);
+        buf.append(basicMessage).append(LINE_SEPARATOR);
+        buf.append(ps.str).append(LINE_SEPARATOR);
+        for (int i = 0; i < ps.pos; i++) {
+            buf.append(" ");
+        }
+        buf.append("^");
+        return buf.toString();
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java
new file mode 100644
index 0000000..8353041
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import static org.cauldron.sigil.model.common.Expressions.and;
+import static org.cauldron.sigil.model.common.Expressions.not;
+import static org.cauldron.sigil.model.common.Expressions.or;
+import static org.cauldron.sigil.model.common.Ops.APPROX;
+import static org.cauldron.sigil.model.common.Ops.EQ;
+import static org.cauldron.sigil.model.common.Ops.GE;
+import static org.cauldron.sigil.model.common.Ops.GT;
+import static org.cauldron.sigil.model.common.Ops.LE;
+import static org.cauldron.sigil.model.common.Ops.LT;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LDAPParser {
+
+    private static final LDAPParser parser = new LDAPParser();
+
+    public static LDAPExpr parseExpression(String strExpr) throws LDAPParseException {
+        return parser.parse(strExpr);
+    }
+
+    public static void main(String[] args) {
+        for (String arg : args) {
+            try {
+                System.out.println(parseExpression(arg));
+            }
+            catch (LDAPParseException e) {
+                System.out.println("Failed to parse " + arg);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public LDAPExpr parse(String strExpr) throws LDAPParseException {
+
+        if (strExpr == null || strExpr.trim().length() == 0) {
+            return LDAPExpr.ACCEPT_ALL;
+        }
+
+        ParseState ps = new ParseState(strExpr);
+        LDAPExpr expr = parseExpr(ps);
+        ps.skipWhitespace();
+        if (!ps.isEndOfString()) {
+            error("expected end of expression ", ps);
+        }
+        return expr;
+    }
+
+    public LDAPExpr parseExpr(ParseState ps) throws LDAPParseException {
+        ps.skipWhitespace();
+        if (!(ps.peek() == '(')) {
+            error("expected (", ps);
+        }
+        ps.read();
+        LDAPExpr expr = null;
+        ps.skipWhitespace();
+        char ch = ps.peek();
+        switch (ch) {
+        case '&':
+            ps.readAndSkipWhiteSpace();
+            List<LDAPExpr> andList = new ArrayList<LDAPExpr>();
+            while (ps.peek() == '(') {
+                andList.add(parseExpr(ps));
+                ps.skipWhitespace();
+            }
+            LDAPExpr[] andArr = andList.toArray(new LDAPExpr[andList.size()]);
+            expr = and(andArr);
+            break;
+        case '|':
+            ps.readAndSkipWhiteSpace();
+            List<LDAPExpr> orList = new ArrayList<LDAPExpr>();
+            while (ps.peek() == '(') {
+                orList.add(parseExpr(ps));
+                ps.skipWhitespace();
+            }
+            LDAPExpr[] orArray = orList.toArray(new LDAPExpr[orList.size()]);
+            expr = or(orArray);
+            break;
+        case '!':
+            ps.readAndSkipWhiteSpace();
+            expr = not(parseExpr(ps));
+            break;
+        default:
+            if (isNameChar(ch)) {
+                expr = parseSimple(ps);
+            }
+            else {
+                error("unexpected character: '" + ch + "'", ps);
+            }
+        }
+        ps.skipWhitespace();
+        if (ps.peek() != ')') {
+            error("expected )", ps);
+        }
+        ps.read();
+        return expr;
+
+    }
+
+    void error(String message, ParseState ps) throws LDAPParseException {
+        throw new LDAPParseException(message, ps);
+    }
+
+    private SimpleTerm parseSimple(ParseState ps) throws LDAPParseException {
+        // read name
+        StringBuffer name = new StringBuffer(16);
+        for (char c = ps.peek(); !ps.isEndOfString() && isNameChar(c); c = ps.peek()) {
+            ps.read();
+            name.append(c);
+        }
+        ps.skipWhitespace();
+        Ops op = null;
+        // read op
+        if (ps.lookingAt("=")) {
+            op = EQ;
+            ps.skip(1);
+        }
+        else if (ps.lookingAt(">=")) {
+            op = GE;
+            ps.skip(2);
+        }
+        else if (ps.lookingAt("<=")) {
+            op = LE;
+            ps.skip(2);
+        }
+        else if (ps.lookingAt(">")) {
+            op = GT;
+            ps.skip(1);
+        }
+        else if (ps.lookingAt("<")) {
+            op = LT;
+            ps.skip(1);
+        }
+        else if (ps.lookingAt("-=")) {
+            op = APPROX;
+            ps.skip(2);
+        }
+        else if (ps.isEndOfString()) {
+            error("unexpected end of expression", ps);
+        }
+        else {
+            error("unexpected character: '" + ps.peek() + "'", ps);
+        }
+        ps.skipWhitespace();
+
+        boolean escaped = false;
+        StringBuffer value = new StringBuffer(16);
+
+        while (!ps.isEndOfString() && !Character.isWhitespace(ps.peek()) && !(ps.peek() == ')' && !escaped)) {
+
+            char ch = ps.peek();
+
+            if (ch == '\\') {
+                escaped = true;
+                ps.read();
+            }
+            else if (ch == '*') {
+                if (escaped) {
+                    value.append(ch);
+                    escaped = false;
+                }
+                else {
+                    value.append(SimpleTerm.WILDCARD);
+                }
+                ps.read();
+            }
+            else if (isLiteralValue(ch)) {
+                if (escaped) {
+                    error("incorrectly applied escape of '" + ch + "'", ps);
+                }
+                value.append(ps.read());
+            }
+            else if (isEscapedValue(ch)) {
+                if (!escaped) {
+                    error("missing escape for '" + ch + "'", ps);
+                }
+                value.append(ps.read());
+                escaped = false;
+            }
+            else {
+                error("unexpected character: '" + ps.peek() + "'", ps);
+            }
+        }
+        ps.skipWhitespace();
+
+        SimpleTerm expr = new SimpleTerm(name.toString(), op, value.toString());
+
+        return expr;
+    }
+
+    private boolean isNameChar(int ch) {
+        return !(Character.isWhitespace(ch) || (ch == '(') || (ch == ')') || (ch == '<') || (ch == '>') || (ch == '=')
+                || (ch == '~') || (ch == '*') || (ch == '\\'));
+    }
+
+    private boolean isLiteralValue(int ch) {
+        return !(Character.isWhitespace(ch) || (ch == '(') || (ch == ')') || (ch == '*'));
+    }
+
+    private boolean isEscapedValue(int ch) {
+        return (ch == '(') || (ch == ')') || (ch == '*');
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java
new file mode 100644
index 0000000..a3b59c3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Not implements LDAPExpr {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    private LDAPExpr[] children;
+
+    public static LDAPExpr apply(LDAPExpr e) {
+        if (e == null) {
+            throw new NullPointerException("cannot apply Not to a null expression");
+        }
+        if (e.equals(Expressions.T)) {
+            return Expressions.F;
+        }
+        if (e.equals(Expressions.F)) {
+            return Expressions.T;
+        }
+        return new Not(e);
+    }
+
+    private Not(LDAPExpr child) {
+        this.children = new LDAPExpr[] { child };
+    }
+
+    public boolean eval(Map<String, ?> map) {
+        return !children[0].eval(map);
+    }
+
+    public LDAPExpr getEx() {
+        return children[0];
+    }
+
+    public void visit(ExprVisitor v) {
+        v.visitNot(this);
+    }
+
+    public LDAPExpr[] getChildren() {
+        return children;
+    }
+
+    public void setChild(LDAPExpr child) {
+        this.children = new LDAPExpr[] { child };
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof Not) {
+            Not that = (Not) other;
+            return children[0].equals(that.children[0]);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer(256);
+        buf.append("(!");
+        buf.append(" ").append(children[0]).append(" ");
+        buf.append(")");
+        return buf.toString();
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java
new file mode 100644
index 0000000..570fe8d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public enum Ops {
+    EQ, GE, LE, GT, LT, APPROX;
+
+    @Override
+    public String toString() {
+        switch (this) {
+        case EQ:
+            return "=";
+        case GE:
+            return ">=";
+        case LE:
+            return "<=";
+        case GT:
+            return ">";
+        case LT:
+            return "<";
+        case APPROX:
+            return "~=";
+        default:
+            return super.toString();
+        }
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java
new file mode 100644
index 0000000..39f3470
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Or implements LDAPExpr {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    private LDAPExpr[] children;
+
+    public static LDAPExpr apply(LDAPExpr... terms) {
+        if (terms == null) {
+            throw new NullPointerException("terms cannot be null");
+        }
+        else if (terms.length == 0) {
+            return Expressions.T;
+        }
+        else if (terms.length == 1) {
+            return terms[0];
+        }
+        LDAPExpr[] filtered = new LDAPExpr[terms.length];
+        int ctr = 0;
+        for (int i = 0; i < terms.length; i++) {
+            if (terms[i].equals(Expressions.T))
+                return Expressions.T;
+            if (terms[i].equals(Expressions.F))
+                continue;
+            filtered[ctr] = terms[i];
+            ctr++;
+        }
+        if (ctr == 0) {
+            return Expressions.F;
+        }
+        else if (ctr == 1) {
+            return filtered[0];
+        }
+        LDAPExpr[] orTerms = new LDAPExpr[ctr];
+        System.arraycopy(filtered, 0, orTerms, 0, ctr);
+
+        return new Or(orTerms);
+    }
+
+    private Or(LDAPExpr... children) {
+        this.children = children;
+    }
+
+    public boolean eval(Map<String, ?> map) {
+        for (int i = 0; i < children.length; i++) {
+            if (children[i].eval(map)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void visit(ExprVisitor v) {
+        v.visitOr(this);
+    }
+
+    public LDAPExpr[] getChildren() {
+        return children;
+    }
+
+    public void setChildren(LDAPExpr[] children) {
+        this.children = children;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof Or) {
+            Or that = (Or) other;
+            if (children.length != that.children.length) {
+                return false;
+            }
+            for (int i = 0; i < children.length; i++) {
+                if (children[i].equals(that.children[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer(256);
+        buf.append("(|");
+        for (int i = 0; i < children.length; i++) {
+            buf.append(" ").append(children[i]).append(" ");
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java
new file mode 100644
index 0000000..a6f23ac
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+/**
+ * @author dave
+ * 
+ */
+class ParseState implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    int pos;
+
+    String str;
+
+    ParseState(String str) {
+        this.str = str;
+    }
+
+    public boolean lookingAt(String start) {
+        return str.substring(pos).startsWith(start);
+    }
+
+    public CharSequence skip(int n) {
+        int end = pos + n < str.length() ? pos + n : str.length();
+        int start = pos;
+        pos = end;
+        return str.subSequence(start, end);
+    }
+
+    public char read() {
+        char ch = str.charAt(pos);
+        if (pos < str.length()) {
+            pos++;
+        }
+        return ch;
+    }
+
+    public char readAndSkipWhiteSpace() {
+        char ch = read();
+        skipWhitespace();
+        return ch;
+    }
+
+    char peek() {
+        if (isEndOfString()) {
+            return (char) -1;
+        }
+        return str.charAt(pos);
+    }
+
+    boolean isEndOfString() {
+        return pos == str.length();
+    }
+
+    void skipWhitespace() {
+        while (pos < str.length() && Character.isWhitespace(str.charAt(pos))) {
+            pos++;
+        }
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java
new file mode 100644
index 0000000..ebc4517
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.lang.reflect.Constructor;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+public class SimpleTerm implements LDAPExpr {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    public static final char WILDCARD = 2 ^ 16 - 1;
+    private static final String WILDCARD_STRING = new String(new char[] { SimpleTerm.WILDCARD });
+
+    private Ops op;
+    private String name;
+    private String rval;
+
+    public SimpleTerm(String name, Ops op, String value) {
+        this.op = op;
+        this.name = name.intern();
+        this.rval = value.intern();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Ops getOp() {
+        return op;
+    }
+
+    public String getRval() {
+        return rval;
+    }
+
+    public boolean eval(Map<String, ?> map) {
+
+        Object lval = map.get(name);
+        if (lval == null) {
+            return false;
+        }
+        else if (Ops.EQ == op && WILDCARD_STRING.equals(lval)) {
+            return true;
+        }
+        // any match in the vector will do
+        else if (lval instanceof Vector<?>) {
+            Vector<?> vec = (Vector<?>) lval;
+            for (Iterator<?> i = vec.iterator(); i.hasNext();) {
+                if (check(i.next())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        // any match in the array will do
+        else if (lval instanceof Object[]) {
+            Object[] arr = (Object[]) lval;
+            for (int i = 0; i < arr.length; i++) {
+                if (check(arr[i])) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return check(lval);
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean check(Object lval) {
+        if (lval == null) {
+            return false;
+        }
+        else if (Ops.EQ == op && WILDCARD_STRING.equals(lval)) {
+            return true;
+        }
+
+        Object rhs = null;
+
+        if (lval instanceof String) {
+
+            if (Ops.APPROX == op) {
+                rhs = collapseWhiteSpace(rval);
+                lval = collapseWhiteSpace((String) lval);
+            }
+
+            if (Ops.EQ == op || Ops.APPROX == op) {
+                return stringCheck((String) lval);
+            }
+            // rhs already a string
+
+        }
+        else if (lval.getClass() == Byte.class) {
+            rhs = Byte.valueOf(rval);
+        }
+        else if (lval.getClass() == Short.class) {
+            rhs = Short.valueOf(rval);
+        }
+        else if (lval.getClass() == Integer.class) {
+            rhs = Integer.valueOf(rval);
+        }
+        else if (lval.getClass() == Long.class) {
+            rhs = Long.valueOf(rval);
+        }
+        else if (lval.getClass() == Float.class) {
+            rhs = Float.valueOf(rval);
+        }
+        else if (lval.getClass() == Double.class) {
+            rhs = Double.valueOf(rval);
+        }
+        else {
+            try {
+                Constructor<?> stringCtor = lval.getClass().getConstructor(new Class[] { String.class });
+                rhs = stringCtor.newInstance(rval);
+            }
+            catch (Exception e) {
+                // log it
+                e.printStackTrace();
+                return false;
+            }
+        }
+
+        if (!(lval instanceof Comparable)) {
+            return Ops.EQ == op && lval.equals(rval);
+        }
+        else {
+
+            Comparable<? super Object> lhs = (Comparable<? super Object>) lval;
+
+            int compare = lhs.compareTo(rhs);
+
+            switch (op) {
+            case EQ:
+                return compare == 0;
+            case APPROX:
+                return compare == 0;
+            case GE:
+                return compare >= 0;
+            case LE:
+                return compare <= 0;
+            case GT:
+                return compare > 0;
+            case LT:
+                return compare < 0;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean stringCheck(String lhs) {
+
+        String rhs;
+        switch (op) {
+        case EQ:
+        case APPROX:
+            rhs = rval;
+            break;
+        default:
+            return false;
+        }
+
+        int valLength = lhs.length();
+        int patLength = rval.length();
+
+        if (valLength == 0 && patLength == 0) {
+            return true;
+        }
+
+        boolean wc = false;
+        int j = 0;
+        for (int i = 0; i < patLength; i++) {
+            // trailing wildcards
+            char pc = rhs.charAt(i);
+            if (j == valLength) {
+                if (pc != SimpleTerm.WILDCARD) {
+                    return false;
+                }
+                continue;
+            }
+            if (pc == SimpleTerm.WILDCARD) {
+                wc = true;
+                continue;
+            }
+            while (wc && j < valLength - 1 && lhs.charAt(j) != pc) {
+                j++;
+            }
+            if (lhs.charAt(j) != pc) {
+                return false;
+            }
+            else {
+                wc = false;
+                j++;
+            }
+        }
+        return (wc || j == valLength);
+
+    }
+
+    private String collapseWhiteSpace(String in) {
+        StringBuffer out = new StringBuffer(in.trim().length());
+        boolean white = false;
+        for (int i = 0; i < in.length(); i++) {
+            char ch = in.charAt(i);
+            if (Character.isWhitespace(ch)) {
+                white = true;
+            }
+            else {
+                if (white) {
+                    out.append(" ");
+                    white = false;
+                }
+                out.append(ch);
+            }
+        }
+        return out.toString();
+    }
+
+    public void visit(ExprVisitor v) {
+        v.visitSimple(this);
+    }
+
+    public LDAPExpr[] getChildren() {
+        return CHILDLESS;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof SimpleTerm) {
+            SimpleTerm that = (SimpleTerm) other;
+            return name.equals(that.name) && op.equals(that.op) && rval.equals(that.rval);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "(" + name + " " + op.toString() + " " + escape(rval) + ")";
+    }
+
+    private String escape(String raw) {
+        StringBuffer buf = new StringBuffer(raw.length() + 10);
+        for (int i = 0; i < raw.length(); i++) {
+            char ch = raw.charAt(i);
+            switch (ch) {
+            case SimpleTerm.WILDCARD:
+                buf.append("*");
+                break;
+            case '(':
+            case ')':
+            case '*':
+                buf.append("\\").append(ch);
+                break;
+            default:
+                buf.append(ch);
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java
new file mode 100644
index 0000000..0b6b30b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Utils {
+    public static MapBuilder map(String name, Object value) {
+        return new MapBuilder().put(name, value);
+    }
+
+    public static String toString(Map<String, Object> attrs) {
+        if (attrs == null) {
+            return "NULL";
+        }
+
+        StringBuffer buf = new StringBuffer(128);
+        List<String> keys = new ArrayList<String>(attrs.keySet());
+        Collections.sort(keys);
+        buf.append("{");
+
+        for (int i = 0; i < keys.size(); i++) {
+            Object name = keys.get(i);
+            Object value = attrs.get(name);
+            buf.append(name).append("=").append(value).append(",");
+        }
+
+        if (buf.length() > 1) {
+            buf.delete(buf.length() - 1, buf.length());
+        }
+
+        buf.append("}");
+
+        return buf.toString();
+    }
+
+    public static class MapBuilder {
+        private Map<String, Object> map = new HashMap<String, Object>();
+
+        public MapBuilder put(String name, Object value) {
+            map.put(name, value);
+
+            return this;
+        }
+
+        public Map<String, Object> toMap() {
+            return map;
+        }
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java
new file mode 100644
index 0000000..3505df4
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+import org.osgi.framework.Version;
+
+public class VersionRange implements Serializable {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+    public static final Version INFINITE_VERSION = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE,
+            Integer.MAX_VALUE, "");
+    public static final VersionRange ANY_VERSION = new VersionRange(false, Version.emptyVersion, INFINITE_VERSION, true);
+
+    private boolean openFloor;
+    private Version floor;
+    private Version ceiling;
+    private boolean openCeiling;
+
+    /**
+     * Interval constructor
+     * 
+     * @param openFloor Whether the lower bound of the range is inclusive (false) or exclusive (true).
+     * @param floor The lower bound version of the range.
+     * @param ceiling The upper bound version of the range.
+     * @param openCeiling Whether the upper bound of the range is inclusive (false) or exclusive (true).
+     */
+    public VersionRange(boolean openFloor, Version floor, Version ceiling, boolean openCeiling) {
+        this.openFloor = openFloor;
+        this.floor = floor;
+        this.ceiling = ceiling;
+        this.openCeiling = openCeiling;
+    }
+
+    /**
+     * atLeast constructor
+     * 
+     * @param openFloor
+     * @param floor
+     */
+    public VersionRange(Version atLeast) {
+        this.openFloor = false;
+        this.floor = atLeast;
+        this.ceiling = INFINITE_VERSION;
+        this.openCeiling = true;
+    }
+	
+    public static VersionRange parseVersionRange(String val) throws IllegalArgumentException, NumberFormatException {
+    	if ( val == null || val.trim().length() == 0 ) {
+    		return ANY_VERSION;
+    	}
+    	
+        boolean openFloor;
+        boolean openCeiling;
+        val = val.replaceAll("\\s", "");
+        val = val.replaceAll("\"", "");
+        int fst = val.charAt(0);
+        if (fst == '[') {
+            openFloor = false;
+        }
+        else if (fst == '(') {
+            openFloor = true;
+        }
+        else {
+            Version atLeast = Version.parseVersion(val);
+            return new VersionRange(atLeast);
+        }
+
+        int lst = val.charAt(val.length() - 1);
+        if (lst == ']') {
+            openCeiling = false;
+        }
+        else if (lst == ')') {
+            openCeiling = true;
+        }
+        else {
+            throw new IllegalArgumentException("illegal version range syntax " + val + ": range must end in ')' or ']'");
+        }
+
+        String inner = val.substring(1, val.length() - 1);
+        String[] floorCeiling = inner.split(",");
+        if (floorCeiling.length != 2) {
+            throw new IllegalArgumentException("illegal version range syntax " + "too many commas");
+        }
+        Version floor = Version.parseVersion(floorCeiling[0]);
+        Version ceiling = "*".equals( floorCeiling[1] ) ? INFINITE_VERSION : Version.parseVersion(floorCeiling[1]);
+        return new VersionRange(openFloor, floor, ceiling, openCeiling);
+    }    
+    public Version getCeiling() {
+        return ceiling;
+    }
+
+    public Version getFloor() {
+        return floor;
+    }
+
+    public boolean isOpenCeiling() {
+        return openCeiling;
+    }
+
+    public boolean isOpenFloor() {
+        return openFloor;
+    }
+
+    public boolean isPointVersion() {
+        return !openFloor && !openCeiling && floor.equals(ceiling);
+    }
+
+    /**
+     * test a version to see if it falls in the range
+     * 
+     * @param version
+     * @return
+     */
+    public boolean contains(Version version) {
+        if (version.equals(INFINITE_VERSION)) {
+            return ceiling.equals(INFINITE_VERSION);
+        }
+        else {
+            return (version.compareTo(floor) > 0 && version.compareTo(ceiling) < 0)
+                    || (!openFloor && version.equals(floor)) || (!openCeiling && version.equals(ceiling));
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((ceiling == null) ? 0 : ceiling.hashCode());
+        result = prime * result + ((floor == null) ? 0 : floor.hashCode());
+        result = prime * result + (openCeiling ? 1231 : 1237);
+        result = prime * result + (openFloor ? 1231 : 1237);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final VersionRange other = (VersionRange) obj;
+        if (ceiling == null) {
+            if (other.ceiling != null)
+                return false;
+        }
+        else if (!ceiling.equals(other.ceiling))
+            return false;
+        if (floor == null) {
+            if (other.floor != null)
+                return false;
+        }
+        else if (!floor.equals(other.floor))
+            return false;
+        if (openCeiling != other.openCeiling)
+            return false;
+        if (openFloor != other.openFloor)
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        if (ANY_VERSION.equals(this)) {
+            return makeString(openFloor, Version.emptyVersion, INFINITE_VERSION, openCeiling);
+        }
+        return makeString(openFloor, floor, ceiling, openCeiling);
+    }
+
+    private String makeString(boolean openFloor, Version floor, Version ceiling, boolean openCeiling) {
+        StringBuffer vr = new StringBuffer(32);
+        if ( INFINITE_VERSION.equals(ceiling) ) {
+        	vr.append( Version.emptyVersion.equals(floor) ? "0" : floor.toString() );
+        }
+        else {
+            vr.append(openFloor ? "(" : "[");
+            String floorStr = Version.emptyVersion.equals(floor) ? "0" : floor.toString();
+            String ceilingStr = ceiling.toString();
+            vr.append(floorStr).append(",").append(ceilingStr);
+            vr.append(openCeiling ? ")" : "]");
+        }
+        return vr.toString();
+    }
+
+    
+    public static VersionRange newInstance(Version pointVersion, VersionRangeBoundingRule lowerBoundRule, VersionRangeBoundingRule upperBoundRule) {
+    	Version floor = null;
+    	switch (lowerBoundRule) {
+		case Any:
+			floor = new Version(0, 0, 0);
+			break;
+		case Major:
+			floor = new Version(pointVersion.getMajor(), 0, 0);
+			break;
+		case Minor:
+			floor = new Version(pointVersion.getMajor(), pointVersion.getMinor(), 0);
+			break;
+		case Micro:
+			floor = new Version(pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro());
+			break;
+		case Exact:
+			floor = pointVersion;
+			break;
+		}
+    	
+    	Version ceiling = null;
+    	boolean openCeiling = true;
+    	switch (upperBoundRule) {
+		case Any:
+			ceiling = INFINITE_VERSION;
+			break;
+		case Major:
+			ceiling = new Version(pointVersion.getMajor() + 1, 0, 0);
+			break;
+		case Minor:
+			ceiling = new Version(pointVersion.getMajor(), pointVersion.getMinor() + 1, 0);
+			break;
+		case Micro:
+			ceiling = new Version(pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro() + 1);
+			break;
+		case Exact:
+			ceiling = pointVersion;
+			openCeiling = false;
+			break;
+		}
+    	
+    	return new VersionRange(false, floor, ceiling, openCeiling);
+    }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java
new file mode 100644
index 0000000..a92139d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public enum VersionRangeBoundingRule {
+	Exact, Micro, Minor, Major, Any
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java
new file mode 100644
index 0000000..aa2b25a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+public interface IDownloadJar extends IModelElement {
+	void addEntry(IPath entry);
+	void removeEntry(IPath entry);
+	// XXX bad spelling on purpose so that ModelElementSupport picks up method
+	// TODO fix in ModelElementSupport
+	Set<IPath> getEntrys();
+	
+	void clearEntries();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java
new file mode 100644
index 0000000..b89dd1c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.osgi.framework.Version;
+
+public interface ILibrary extends IModelElement {
+	String getName();
+	void setName(String name);
+	Version getVersion();
+	void setVersion(Version version);
+	void addImport(IPackageImport pi);
+	void removeImport(IPackageImport pi);
+	Collection<IPackageImport> getImports();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java
new file mode 100644
index 0000000..8d53c39
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface ILibraryImport extends IModelElement {
+	String getLibraryName();
+	void setLibraryName(String name);
+	VersionRange getVersions();
+	void setVersions(VersionRange range);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java
new file mode 100644
index 0000000..38e14c6
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * @author dave
+ *
+ */
+public interface INewtonSystem extends IModelElement {
+    IPath getLocation();
+    
+    void setLocation(IPath location);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java
new file mode 100644
index 0000000..6205744
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * @author dave
+ *
+ */
+public interface ISCAComposite extends IModelElement {
+    IPath getLocation();
+    
+    void setLocation(IPath location);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java
new file mode 100644
index 0000000..54a80dd
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IVersionedModelElement;
+
+/**
+ * @author dave
+ *
+ */
+public interface ISigilBundle extends ICompoundModelElement, IVersionedModelElement {
+	void synchronize(IProgressMonitor monitor) throws IOException;
+	
+	boolean isSynchronized();	
+	
+    IBundleModelElement getBundleInfo();
+    
+	String getSymbolicName();
+    
+    void setBundleInfo(IBundleModelElement bundle);
+    
+    IDownloadJar getDownloadJar();
+    
+    void setDownloadJar(IDownloadJar download);
+    
+    void addComposite(ISCAComposite composite);
+    
+    void removeComposite(ISCAComposite composite);
+    
+    Set<ISCAComposite> getComposites();
+
+    void addLibraryPath(IPath path);
+
+    void removeLibraryPath(IPath path);
+
+    Set<IPath> getLibraryPaths();
+
+    void addSourcePath(IPath path);
+
+    void removeSourcePath(IPath path);
+
+    Set<IPath> getSourcePaths();
+
+	void clearSourcePaths();
+
+	Set<String> getClasspathEntrys();
+	
+	void addClasspathEntry(String encodedClasspath);
+	
+	void removeClasspathEntry(String encodedClasspath);
+
+	IPath getLocation();
+	
+	void setLocation(IPath location);
+
+	IPath getSourcePathLocation();
+	
+	void setSourcePathLocation( IPath location );
+
+	IPath getSourceRootPath();
+	
+	void setSourceRootPath( IPath location );
+
+	void setLicencePathLocation(IPath cacheSourceLocation);
+	
+	IPath getLicencePathLocation();
+	
+	/**
+	 * get package names included in bundle.
+	 * Can contain wildcards e.g. org.foo.*
+	 */
+	Set<String> getPackages();
+	
+	/**
+	 * remove package name from those included in bundle.
+	 */
+	boolean removePackage(String pkg);
+	
+	/**
+	 * add package name to be included in bundle.
+	 */
+	void addPackage(String pkg);
+	
+	
+	/**
+	 * get package names included in download jar.
+	 * Can contain wildcards e.g. org.foo.*
+	 */
+	Set<String> getDownloadPackages();
+	
+	/**
+	 * remove package name from those included in download jar.
+	 */
+	boolean removeDownloadPackage(String pkg);
+	
+	/**
+	 * add package name to be included in download jar.
+	 */
+	void addDownloadPackage(String pkg);
+
+	/**
+	 * Attempt to find a package export that matches the given name or return null if none specified
+	 * 
+	 * @param elementName
+	 * @return
+	 */
+	IPackageExport findExport(String elementName);
+
+	/**
+	 * Attempt to find a package import that matches the given name or return null if none specified
+	 * @param packageName
+	 * @return
+	 */
+	IPackageImport findImport(String packageName);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java
new file mode 100644
index 0000000..1f2b8ad
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Set;
+
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.INamedModelElement;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.osgi.framework.Version;
+
+public interface IBundleModelElement extends INamedModelElement, ICompoundModelElement, IVersionedModelElement {
+
+	 String getActivator();
+	 
+	 void setActivator(String activator);
+	 
+	 String getCategory();
+
+	 void setCategory(String category);
+
+	 String getContactAddress();
+
+	 void setContactAddress(String contactAddress);
+
+	 String getCopyright();
+
+	 void setCopyright(String copyright);
+
+	 URI getDocURI();
+
+	 void setDocURI(URI docURI);
+
+	 Set<IPackageExport> getExports();
+
+	 void addExport(IPackageExport packageExport);
+
+	 void removeExport(IPackageExport packageExport);
+
+	 Set<IPackageImport> getImports();
+
+	 void addImport(IPackageImport packageImport);
+
+	 void removeImport(IPackageImport packageImport);
+
+	 Set<IRequiredBundle> getRequiredBundles();
+
+	 void addRequiredBundle(IRequiredBundle bundle);
+
+	 void removeRequiredBundle(IRequiredBundle bundle);
+	 
+	 void addLibraryImport(ILibraryImport library);
+	 
+	 void removeLibraryImport(ILibraryImport library);
+	 
+	 Set<ILibraryImport> getLibraryImports();
+
+	 URI getLicenseURI();
+
+	 void setLicenseURI(URI licenseURI);
+
+	 URI getSourceLocation();
+
+	 void setSourceLocation(URI sourceLocation);
+
+	 String getSymbolicName();
+
+	 void setSymbolicName(String symbolicName);
+
+	 URI getUpdateLocation();
+
+	 void setUpdateLocation(URI updateLocation);
+
+	 String getVendor();
+
+	 void setVendor(String vendor);
+
+	 Version getVersion();
+
+	 void setVersion(Version version);
+
+	 void setDescription(String elementText);
+	 
+	 String getDescription();
+
+	Collection<String> getClasspaths();
+	
+	void addClasspath(String path);
+	
+	void removeClasspath(String path);
+
+	void setFragmentHost(IRequiredBundle fragmentHost);
+	
+	IRequiredBundle getFragmentHost();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java
new file mode 100644
index 0000000..008b56e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import java.util.Collection;
+
+import org.osgi.framework.Version;
+
+public interface IPackageExport extends IPackageModelElement, IVersionedModelElement, Comparable<IPackageExport> {
+	void addUse(String uses);
+	
+	void removeUse(String uses);
+	
+	Collection<String> getUses();
+
+	void setUses(Collection<String> asList);
+	
+	Version getRawVersion();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java
new file mode 100644
index 0000000..c9a7704
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IRequirementModelElement;
+
+public interface IPackageImport extends IPackageModelElement, IVersionRangeModelElement, IRequirementModelElement, Comparable<IPackageImport> {
+	/**
+	 * indicates whether the OSGi attribute "resolution=optional" is specified.
+	 */
+	boolean isOptional();
+	
+	void setOptional(boolean optional);
+
+	/**
+	 * indicates whether import is needed at compile-time.
+	 * Default true. Used in conjunction with OSGiHeader.ALWAYS,
+	 * to add an OSGI import, without creating a dependency.
+	 */
+	boolean isDependency();
+	
+	void setDependency(boolean dependency);
+	
+	/**
+	 * indicates whether import should be added to OSGi Package-Import header.
+	 * Default: AUTO.
+	 */
+	OSGiImport getOSGiImport();
+	
+	void setOSGiImport(OSGiImport osgiImport);
+	
+	enum OSGiImport {
+		/**
+		 * only add to OSGi header, if it appears to be needed.
+		 */
+		AUTO,
+		
+		/**
+		 * always add to OSGi header, even if it appears unnecessary.
+		 */
+		ALWAYS,
+		
+		/**
+		 * never add to OSGi header.
+		 */
+		NEVER
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java
new file mode 100644
index 0000000..3e40d45
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public interface IPackageModelElement extends IModelElement {
+
+	String getPackageName();
+
+	void setPackageName(String packageName);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java
new file mode 100644
index 0000000..9f75a43
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IRequirementModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface IRequiredBundle extends IModelElement, IRequirementModelElement, Comparable<IRequiredBundle> {
+	String getSymbolicName();
+
+	void setSymbolicName(String symbolicName);
+
+	VersionRange getVersions();
+
+	void setVersions(VersionRange versions);
+
+	boolean isOptional();
+	
+	void setOptional(boolean optional);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java
new file mode 100644
index 0000000..dfc7382
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface IVersionRangeModelElement {
+
+	VersionRange getVersions();
+
+	void setVersions(VersionRange version);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java
new file mode 100644
index 0000000..10c60c9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.osgi.framework.Version;
+
+public interface IVersionedModelElement {
+
+	Version getVersion();
+
+	void setVersion(Version version);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java
new file mode 100644
index 0000000..850de43
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.bld.core.util.QuoteUtil;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public abstract class AbstractBundleRepository implements IBundleRepository {
+	
+	private final String id;
+	private final HashSet<IBundleRepositoryListener> listeners = new HashSet<IBundleRepositoryListener>();
+
+	public AbstractBundleRepository(String id) {
+		this.id = id;
+	}
+	
+	public abstract void accept(IRepositoryVisitor visitor, int options);
+	
+	public void addBundleRepositoryListener(IBundleRepositoryListener listener) {
+		synchronized(listeners) {
+			listeners.add(listener);
+		}
+	}
+
+	public void removeBundleRepositoryListener(IBundleRepositoryListener listener) {
+		synchronized(listeners) {
+			listeners.remove(listener);
+		}
+	}
+	
+	protected void notifyChange() {
+		for ( IBundleRepositoryListener l : listeners ) {
+			l.notifyChange(this);
+		}
+	}
+	
+	public String getId() {
+		return id;
+	}
+		
+	public void accept(IRepositoryVisitor visitor) {
+		accept( visitor, 0 );
+	}
+	
+	public void writeOBR(OutputStream out) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	public Collection<ISigilBundle> findProviders(final ILibrary library, int options) {
+		final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		final ILicensePolicy policy = findPolicy(library);
+		
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				if (policy.accept(bundle)) {
+					IBundleModelElement info = bundle.getBundleInfo();
+					for ( IPackageImport pi : library.getImports() ) {
+						for ( IPackageExport e : info.getExports() ) {
+							if ( pi.getPackageName().equals( e.getPackageName() ) && pi.getVersions().contains( e.getVersion() ) ) {
+								found.add(bundle);
+								break;
+							}
+						}
+					}					
+				}
+				return true;
+			}
+		};
+		
+		accept( visitor, options );
+		
+		return found;
+	}
+
+	public Collection<ISigilBundle> findAllProviders(final IRequiredBundle req, int options) {
+		final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		final ILicensePolicy policy = findPolicy(req);
+		
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				if (policy.accept(bundle)) {
+					IBundleModelElement info = bundle.getBundleInfo();
+					if ( req.getSymbolicName().equals( info.getSymbolicName() ) && req.getVersions().contains( info.getVersion() ) ) {
+						found.add(bundle);
+					}
+				}
+				return true;
+			}
+		};
+		
+		accept( visitor, options );
+		
+		return found;
+	}
+
+	public Collection<ISigilBundle> findAllProviders(final IPackageImport pi, int options) {
+		final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		final ILicensePolicy policy = findPolicy(pi);
+		
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+
+			public boolean visit(ISigilBundle bundle) {
+				if (policy.accept(bundle)) {
+					IBundleModelElement info = bundle.getBundleInfo();
+					if ( info != null ) {
+						for ( IPackageExport e : info.getExports() ) {
+							if ( pi.getPackageName().equals( e.getPackageName() ) ) {
+								if ( pi.getVersions().contains( e.getVersion() ) ) {
+									found.add(bundle);
+									break;
+								}
+							}
+						}
+					}
+				}
+				return true;
+			}
+			
+		};
+		
+		accept( visitor, options );
+		
+		return found;
+	}
+
+	public ISigilBundle findProvider(final IPackageImport pi, int options) {
+		final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		final ILicensePolicy policy = findPolicy(pi);
+		
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				if (policy.accept(bundle)) {
+					IBundleModelElement info = bundle.getBundleInfo();
+					for ( IPackageExport e : info.getExports() ) {
+						if ( pi.getPackageName().equals( e.getPackageName() ) && pi.getVersions().contains( e.getVersion() ) ) {
+							found.add( bundle );
+							return false;
+						}
+					}
+				}
+				return true;
+			}
+			
+		};
+		
+		accept( visitor, options );
+		
+		return found.isEmpty() ? null : found.iterator().next();
+	}
+
+	public ISigilBundle findProvider(final IRequiredBundle req, int options) {
+		final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+		
+		final ILicensePolicy policy = findPolicy(req);
+		
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+
+			public boolean visit(ISigilBundle bundle) {
+				if (policy.accept(bundle)) {
+					IBundleModelElement info = bundle.getBundleInfo();
+					if ( req.getSymbolicName().equals( info.getSymbolicName() ) && req.getVersions().contains( info.getVersion() ) ) {
+						found.add( bundle );
+						return false;
+					}
+				}
+				return true;
+			}
+			
+		};
+		
+		accept( visitor, options );
+		
+		return found.isEmpty() ? null : found.iterator().next();
+	}
+	
+	public IBundleModelElement buildBundleModelElement(Manifest mf) {
+		IBundleModelElement info = null;
+		
+		if ( mf != null ) {
+			Attributes attrs = mf.getMainAttributes();
+			String name = attrs.getValue("Bundle-SymbolicName");
+			if (name == null) {
+    			// framework.jar doesn't have Bundle-SymbolicName!
+    			name = attrs.getValue("Bundle-Name");
+			}
+			
+			if (name != null) {
+				try {
+					info = ModelElementFactory.getInstance().newModelElement( IBundleModelElement.class );
+					info.setSymbolicName( name.split(";")[0] );
+					info.setVersion( Version.parseVersion( attrs.getValue( "Bundle-Version" ) ) );
+					info.setName( attrs.getValue( "Bundle-Name" ) );
+					info.setDescription( attrs.getValue( "Bundle-Description" ) );
+					info.setVendor( attrs.getValue( "Bundle-Vendor" ) );
+
+					String importStr = attrs.getValue( "Import-Package" );
+					if ( importStr != null ) {
+						addImports( info, importStr );
+					}
+					String exportStr = attrs.getValue( "Export-Package" );
+					if ( exportStr != null ) {
+						addExports( info, exportStr );
+					}
+
+					String reqStr = attrs.getValue( "Require-Bundle" );
+					if ( reqStr != null ) {
+						addRequires( info, reqStr );
+					}
+
+					String cpStr = attrs.getValue( "Bundle-Classpath" );
+
+					if ( cpStr != null ) {
+						addClasspath( info, cpStr );
+					}
+				}
+				catch (RuntimeException e) {
+					BldCore.error( "Failed to read info from bundle " + name, e );
+					// clear elements as clearly got garbage
+					info = null;
+				}
+			}
+		}
+		
+		return info;
+	}
+
+	protected ILicensePolicy findPolicy(IModelElement elem) {
+		ILicenseManager man = BldCore.getLicenseManager();
+		
+/*		ISigilProjectModel p = elem.getAncestor(ISigilProjectModel.class);
+		
+		ILicensePolicy policy = null;
+		
+		if ( p != null ) {
+			policy = man.getPolicy(p);
+		}
+		else {
+			policy = man.getDefaultPolicy();
+		}
+		
+		return policy; */
+		
+		return man.getDefaultPolicy();
+	}
+	
+	private void addClasspath(IBundleModelElement info, String cpStr) {
+		for ( String cp : cpStr.split( "," ) ) {
+			info.addClasspath( cp );
+		}
+	}
+
+	private void addExports(IBundleModelElement info, String exportStr) throws ModelElementFactoryException {
+		for ( String exp : QuoteUtil.split( exportStr ) ) {
+			try { 
+				String[] parts = exp.split( ";" );
+				IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+				pe.setPackageName( parts[0].trim() );
+				
+				if ( parts.length > 1 ) {
+					for (int i = 1; i < parts.length; i++ ) {
+						String check = parts[i];
+						if ( check.toLowerCase().startsWith( "version=" ) ) {
+							pe.setVersion( parseVersion(check.substring("version=".length())));
+						}
+						else if ( check.toLowerCase().startsWith( "specification-version=" ) ) {
+							pe.setVersion( parseVersion( check.substring("specification-version=".length()) ) );
+						}
+						else if ( check.toLowerCase().startsWith( "uses:=" ) ) {
+							for (String use : parseUses( check.substring( "uses:=".length() ) ) ) {
+								pe.addUse(use);
+							}
+						}
+					}
+				}
+				info.addExport(pe);
+			}
+			catch (RuntimeException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	private Collection<String> parseUses(String uses) {
+		if ( uses.startsWith( "\"") ) {
+			uses = uses.substring(1, uses.length() - 2 );
+		}
+		
+		return Arrays.asList( uses.split(",") );
+	}
+
+	private Version parseVersion(String val) {
+        val = val.replaceAll("\"", "");
+        return new Version(val);
+	}
+
+	private void addImports(IBundleModelElement info, String importStr) throws ModelElementFactoryException {
+		for ( String imp : QuoteUtil.split( importStr ) ) {
+			String[] parts = imp.split( ";" );
+			IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+			pi.setPackageName( parts[0].trim() );
+			
+			if ( parts.length > 1 ) {
+				for ( int i = 1; i < parts.length; i++ ) {
+					String p = parts[i];
+					if ( p.toLowerCase().startsWith( "version=" ) ) {
+						pi.setVersions( VersionRange.parseVersionRange(p.substring("version=".length())));
+					}
+					else if ( p.toLowerCase().startsWith( "specification-version=" ) ) {
+						pi.setVersions( VersionRange.parseVersionRange( p.substring("specification-version=".length()) ));
+					}
+					else if ( p.toLowerCase().startsWith( "resolution:=" ) ) {
+						pi.setOptional( p.toLowerCase().substring("resolution:=".length()).equals( "optional") );
+					}
+				}
+			}
+			info.addImport(pi);
+		}
+	}
+
+	private void addRequires(IBundleModelElement info, String reqStr) throws ModelElementFactoryException {
+		for ( String imp : QuoteUtil.split( reqStr ) ) {
+			String[] parts = imp.split( ";" );
+			IRequiredBundle req = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+			req.setSymbolicName( parts[0] );
+			
+			if ( parts.length > 1 ) {
+				if ( parts[1].toLowerCase().startsWith( "version=" ) ) {
+					req.setVersions( VersionRange.parseVersionRange(parts[1].substring("version=".length())));
+				}
+				else if ( parts[1].toLowerCase().startsWith( "specification-version=" ) ) {
+					req.setVersions( VersionRange.parseVersionRange( parts[1].substring("specification-version=".length()) ));
+				}
+			}
+			info.addRequiredBundle(req);
+		}
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java
new file mode 100644
index 0000000..a4d09e8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.cauldron.bld.core.repository.BundleResolver;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.RepositoryChangeEvent.Type;
+
+public abstract class AbstractRepositoryManager implements IRepositoryManager, IBundleRepositoryListener {
+
+	private HashSet<IRepositoryChangeListener> listeners = new HashSet<IRepositoryChangeListener>();
+	
+	private boolean initialised;
+	
+	private HashMap<String, IBundleRepository> repositories = new HashMap<String, IBundleRepository>();
+	private ArrayList<IBundleRepository> order = new ArrayList<IBundleRepository>();
+	private TreeMap<Integer, HashSet<IBundleRepository>> levelMap = new TreeMap<Integer, HashSet<IBundleRepository>>();
+	private int[] levels;
+	
+	private BundleResolver resolver = new BundleResolver(this);
+	
+	private ArrayList<ILibrary> libraries = new ArrayList<ILibrary>();
+	
+	public void initialise() {
+		synchronized( repositories ) {
+			if ( !initialised ) {
+				initialised = true;
+				loadRepositories();
+			}
+		}
+	}
+	
+	protected abstract void loadRepositories();
+
+	public void addRepositoryChangeListener(IRepositoryChangeListener listener) {
+		synchronized(listeners) {
+			listeners.add(listener);			
+		}
+	}
+
+	public void removeRepositoryChangeListener(IRepositoryChangeListener listener) {
+		synchronized(listeners) {
+			listeners.remove(listener);
+		}
+	}
+	
+	public void notifyChange(IBundleRepository repository) {
+		notifyListeners( new RepositoryChangeEvent(repository, Type.CHANGED ) );
+	}
+
+	private void notifyListeners(RepositoryChangeEvent event) {
+		ArrayList<IRepositoryChangeListener> safe = null;
+		synchronized(listeners) {
+			safe = new ArrayList<IRepositoryChangeListener>(listeners);
+		}
+		for ( IRepositoryChangeListener l : safe ) {
+			l.repositoryChanged(event);
+		}
+	}
+	
+	protected void setRepositories(IBundleRepository[] repos) {
+		synchronized( repositories ) {
+			repositories.clear();
+			order.clear();
+			levelMap.clear();
+			resetLevels();
+			if ( repos != null ) {
+				for ( int i = 0; i < repos.length; i++ ) {
+					addRepository(repos[i], i);
+				}
+			}
+		}
+	}
+
+	protected void addRepository(IBundleRepository rep, int level) {
+		Type type = null;
+		
+		synchronized( repositories ) {
+			IBundleRepository old = repositories.put(rep.getId(), rep); 
+			if ( old == null ) {
+				type = Type.ADDED;
+				rep.addBundleRepositoryListener(this);
+			}
+			else {
+				old.removeBundleRepositoryListener(this);
+				type = Type.CHANGED;
+				order.remove(old);
+				clearLevel(rep);
+			}
+			
+			order.add(rep);
+			
+			HashSet<IBundleRepository> set = levelMap.get(level);
+			
+			if ( set == null ) {
+				set = new HashSet<IBundleRepository>();
+				levelMap.put( level, set );
+			}
+			
+			set.add( rep );
+			resetLevels();
+		}
+		
+		notifyListeners( new RepositoryChangeEvent(rep, type ) );
+	}
+	
+	protected void removeRepository(IBundleRepository rep) {
+		Type type = null;
+		
+		synchronized( repositories ) {
+			if ( repositories.remove(rep.getId()) != null ) {
+				order.remove(rep);
+				type = Type.REMOVED;
+				clearLevel(rep);
+				resetLevels();
+			}
+		}
+		
+		if ( type != null ) {
+			notifyListeners( new RepositoryChangeEvent(rep, type ) );
+		}
+	}
+	
+	private void clearLevel(IBundleRepository rep) {
+		for ( Iterator<Map.Entry<Integer, HashSet<IBundleRepository>>> iter = levelMap.entrySet().iterator(); iter.hasNext(); ) {
+			Map.Entry<Integer, HashSet<IBundleRepository>> e = iter.next();
+			if ( e.getValue().remove(rep) ) {
+				if ( e.getValue().isEmpty() ) {
+					iter.remove();
+				}					
+				break;
+			}				
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.repository.IRepositoryManager#getRepositories()
+	 */
+	public Collection<IBundleRepository> getRepositories() {
+		initialise();
+		ArrayList<IBundleRepository> safe = null;
+		
+		synchronized( repositories ) {
+			safe = new ArrayList<IBundleRepository>( order );
+		}
+		
+		return safe;
+	}
+
+	private void resetLevels() {
+		Collections.sort(order, new Comparator<IBundleRepository>() {
+			public int compare(IBundleRepository o1, IBundleRepository o2) {
+				int l1 = findLevel(o1);
+				int l2 = findLevel(o2);
+				
+				if ( l1 < l2 ) {
+					return -1;
+				}
+				else if ( l1 > l2 ) {
+					return 1;
+				}
+				else {
+					return 0;
+				}
+			}
+
+			private int findLevel(IBundleRepository rep) {
+				for ( Map.Entry<Integer, HashSet<IBundleRepository>> e : levelMap.entrySet() ) {
+					if ( e.getValue().contains( rep ) ) {
+						return e.getKey();
+					}
+				}
+				throw new IllegalStateException();
+			}
+		});
+		levels = new int[levelMap.size()];
+		int i = 0;
+		for ( Integer v : levelMap.keySet() ) {
+			levels[i++] = v;
+		}			
+	}
+	
+
+	public int[] getPriorityLevels() {
+		initialise();
+		synchronized( repositories ) {
+			return levels;
+		}
+	}
+
+	public Collection<IBundleRepository> getRepositories(int priorityLevel) {
+		initialise();
+		List<IBundleRepository> found = null;
+		
+		synchronized (repositories) {
+			HashSet<IBundleRepository> b = levelMap.get(priorityLevel);
+			if ( b == null ) {
+				found = Collections.emptyList();
+			}
+			else {
+				found = new ArrayList<IBundleRepository>(b);
+			}
+		}
+		
+		return found;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.repository.IRepositoryManager#addLibrary(org.cauldron.sigil.model.eclipse.ILibrary)
+	 */
+	public void addLibrary(ILibrary library) {
+		synchronized( libraries ) {
+			libraries.add(library);
+		}
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.repository.IRepositoryManager#removeLibrary(org.cauldron.sigil.model.eclipse.ILibrary)
+	 */
+	public void removeLibrary(ILibrary library) {
+		synchronized( libraries ) {
+			libraries.remove(library);
+		}
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.repository.IRepositoryManager#getLibraries()
+	 */
+	public Collection<ILibrary> getLibraries() {
+		synchronized( libraries ) {
+			return libraries;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.internal.repository.IRepositoryManager#resolveLibrary(org.cauldron.sigil.model.eclipse.ILibraryImport)
+	 */
+	public ILibrary resolveLibrary(final ILibraryImport l) {
+		final ArrayList<ILibrary> found = new ArrayList<ILibrary>(1);
+		//ISigilProjectModel p = l.getAncestor(ISigilProjectModel.class);
+		//
+		//IModelWalker w = new IModelWalker() {
+		//	public boolean visit(IModelElement element) {
+		//		if ( element instanceof ILibrary ) {
+		//			updateLibrary(l, (ILibrary) element, found);
+		//			return false;
+		//		}
+		//		
+		//		return true;
+		//	}
+		//};
+		
+		//p.visit( w );
+		
+		//if ( found.isEmpty() ) { // no project specific libraries - check workspace definitions
+			synchronized( libraries ) {
+				for ( ILibrary lib : libraries ) {
+					if ( l.getLibraryName().equals( lib.getName() ) && l.getVersions().contains(lib.getVersion()) ) {
+						updateLibrary(l, lib, found);
+					}
+				}
+			}
+		//}
+		
+		return found.isEmpty() ? null : found.get(0);		
+	}
+
+	protected void updateLibrary(ILibraryImport li, ILibrary l, ArrayList<ILibrary> found) {
+		if ( li.getLibraryName().equals( l.getName() ) && li.getVersions().contains(l.getVersion()) ) {
+			if ( found.isEmpty() ) {
+				found.add( l );
+			}
+			else {
+				ILibrary c = found.get(0);
+				if ( l.getVersion().compareTo(c.getVersion()) > 0 ) {
+					found.remove(0);
+					found.add(l);
+				}
+			}
+		}
+	}
+
+	public IBundleResolver getBundleResolver() {
+		return resolver;
+	}
+
+	public void visit(final IModelWalker walker) {
+		for (IBundleRepository rep : getRepositories()) {
+			IRepositoryVisitor wrapper = new IRepositoryVisitor() {
+				public boolean visit(ISigilBundle bundle) {
+					bundle.visit(walker);
+					// return true as still want to visit other bundles
+					return true;
+				}
+			};
+			rep.accept(wrapper);
+		}
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java
new file mode 100644
index 0000000..ed7ddc1
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IBundleRepository {
+	static final int NORMAL_PRIORITY = 0;
+	static final int MAXIMUM_PRIORITY = -500;
+	static final int MINIMUM_PRIORITY = 500;
+	
+	String getId();
+	
+	void addBundleRepositoryListener(IBundleRepositoryListener listener);
+	
+	void removeBundleRepositoryListener(IBundleRepositoryListener listener);
+	
+	void accept(IRepositoryVisitor visitor);
+	
+	void accept(IRepositoryVisitor visitor, int options);
+	
+	void writeOBR(OutputStream out) throws IOException;
+	
+	void refresh();
+	
+	ISigilBundle findProvider(IPackageImport packageImport, int options);
+	
+	ISigilBundle findProvider(IRequiredBundle bundle, int options);
+	
+	Collection<ISigilBundle> findAllProviders(IRequiredBundle bundle, int options);
+	
+	Collection<ISigilBundle> findAllProviders(IPackageImport packageImport, int options);
+	
+	Collection<ISigilBundle> findProviders(ILibrary library, int options);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java
new file mode 100644
index 0000000..33c8e6c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IBundleRepositoryListener {
+	void notifyChange(IBundleRepository repository);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java
new file mode 100644
index 0000000..f42d372
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public interface IBundleResolver {
+	IResolution resolve(IModelElement element, ResolutionConfig config, IResolutionMonitor monitor) throws ResolutionException;
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java
new file mode 100644
index 0000000..7f2eba3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IProviderChangeListener {
+	void notifyChange(IRepositoryProvider provider);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java
new file mode 100644
index 0000000..f8964f3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IRepositoryChangeListener {
+	void repositoryChanged(RepositoryChangeEvent event);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java
new file mode 100644
index 0000000..f4a5ede
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.eclipse.core.runtime.CoreException;
+
+public interface IRepositoryManager {
+	void addRepositoryChangeListener(IRepositoryChangeListener listener);
+	
+	void removeRepositoryChangeListener(IRepositoryChangeListener listener);
+	
+	Collection<IBundleRepository> getRepositories();
+	
+	Collection<IBundleRepository> getRepositories(int level);
+	
+	void addLibrary(ILibrary library);
+
+	void removeLibrary(ILibrary library);
+
+	Collection<ILibrary> getLibraries();
+
+	ILibrary resolveLibrary(final ILibraryImport l);
+	
+	IBundleResolver getBundleResolver();
+
+	int[] getPriorityLevels();
+
+	void visit(IModelWalker modelWalker);	
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java
new file mode 100644
index 0000000..856a8b4
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.Properties;
+
+public interface IRepositoryProvider {
+    IBundleRepository createRepository(String id, Properties properties) throws RepositoryException;
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java
new file mode 100644
index 0000000..1768815
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IRepositoryVisitor {
+	/**
+	 * Visit the next bundle in the repository. 
+	 * Return true if should continue visiting other bundles, false otherwise.
+	 * @param bundle
+	 * @return
+	 */
+	boolean visit(ISigilBundle bundle);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java
new file mode 100644
index 0000000..51360c7
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IResolution {
+	Set<ISigilBundle> getBundles();
+	ISigilBundle getProvider(IModelElement requirement);
+	List<IModelElement> getMatchedRequirements(ISigilBundle bundle);
+	void synchronize(IProgressMonitor monitor);
+	boolean isSynchronized();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java
new file mode 100644
index 0000000..9a739c2
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IResolutionMonitor {
+	boolean isCanceled();
+	void startResolution(IModelElement requirement);
+	void endResolution(IModelElement requirement, ISigilBundle sigilBundle);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java
new file mode 100644
index 0000000..476b219
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class RepositoryChangeEvent {
+	public static enum Type { ADDED, CHANGED, REMOVED };
+	
+	private Type type;
+	private IBundleRepository repository;
+	
+	public RepositoryChangeEvent(IBundleRepository repository, Type type) {
+		this.repository = repository;
+		this.type = type;
+	}
+	
+	public Type getType() {
+		return type;
+	}
+	public IBundleRepository getRepository() {
+		return repository;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java
new file mode 100644
index 0000000..a9a71c5
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class RepositoryException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+
+	public RepositoryException(String msg, Throwable cause) {
+		super(msg, cause);
+	}
+
+	public RepositoryException(String message) {
+		super(message);
+	}
+
+	public RepositoryException(Throwable cause) {
+		super(cause);
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java
new file mode 100644
index 0000000..ae90a75
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class ResolutionConfig {
+	private int options;
+	
+	public static final int INCLUDE_DEPENDENTS = 1;
+	public static final int INCLUDE_OPTIONAL = 2;
+	public static final int IGNORE_ERRORS = 4;
+	/** Return only bundles that are indexed locally */
+	public static final int INDEXED_ONLY = 8;
+	/** Return only bundles that are stored or cached locally */
+	public static final int LOCAL_ONLY = 16;
+
+	public ResolutionConfig() {
+		this(INCLUDE_DEPENDENTS);
+	}
+	
+	public ResolutionConfig(int options) {
+		this.options = options;
+	}
+
+	public int getOptions() {
+		return options;
+	}
+
+	public boolean isDependents() {
+		return (options & INCLUDE_DEPENDENTS) != 0;
+	}
+
+	public boolean isIgnoreErrors() {
+		return (options & IGNORE_ERRORS) != 0;
+	}
+	
+	public boolean isOptional() {
+		return (options & INCLUDE_OPTIONAL) != 0;
+	}
+
+	public boolean isCalculateLocalDependencies() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java
new file mode 100644
index 0000000..c0c093d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public class ResolutionException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+	
+	private IModelElement[] parsed;
+	
+	public ResolutionException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	public ResolutionException(String message) {
+		super(message);
+	}
+
+	public ResolutionException(Throwable cause) {
+		super(cause);
+	}
+
+	public ResolutionException(IModelElement root, IModelElement[] parsed) {
+		super(buildMessage(root, parsed));
+		this.parsed = parsed;
+	}
+	
+	private static String buildMessage(IModelElement root, IModelElement[] parsed) {
+		StringBuilder b = new StringBuilder();
+		b.append( "Failed to resolve " );
+		b.append( root );
+		
+		if ( parsed.length > 0 ) {
+			b.append( " due to missing provider for " );
+			b.append( parsed[parsed.length - 1] );
+		}
+		
+		return b.toString();
+	}
+
+	public IModelElement[] getParsed() {
+		return parsed;
+	}
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java
new file mode 100644
index 0000000..06348f9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public class ResolutionMonitorAdapter implements IResolutionMonitor {
+
+	private IProgressMonitor monitor;
+	
+	public ResolutionMonitorAdapter(IProgressMonitor monitor) {
+		this.monitor = monitor;
+	}
+
+	public boolean isCanceled() {
+		return monitor.isCanceled();
+	}
+
+	public void startResolution(IModelElement requirement) {
+		monitor.subTask( "Resolving " + requirement);
+	}
+
+	public void endResolution(IModelElement requirement, ISigilBundle provider) {
+		monitor.subTask( (provider == null ? "Failed to resolve " : "Resolved ") + requirement);
+	}
+
+}
diff --git a/sigil/org.cauldron.bld.obr/.classpath b/sigil/org.cauldron.bld.obr/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.obr/.project b/sigil/org.cauldron.bld.obr/.project
new file mode 100644
index 0000000..e4f71db
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.bld.obr</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cd02003
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Wed Sep 10 14:14:56 BST 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..66d1746
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Wed Sep 10 14:14:56 BST 2008
+eclipse.preferences.version=1
+pluginProject.equinox=false
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF b/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..20dfeea
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Obr Plug-in
+Bundle-SymbolicName: org.cauldron.bld.obr
+Bundle-Version: 0.8.0.qualifier
+Bundle-Vendor: Paremus Ltd
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Require-Bundle: org.cauldron.bld.core;bundle-version="0.6.0",
+ org.eclipse.equinox.common;bundle-version="3.4.0"
+Import-Package: org.osgi.framework;version="1.4.0"
+Export-Package: org.cauldron.bld.obr
diff --git a/sigil/org.cauldron.bld.obr/build.properties b/sigil/org.cauldron.bld.obr/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java
new file mode 100644
index 0000000..71c033d
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+
+public abstract class AbstractOBRBundleRepository extends AbstractBundleRepository {
+	private static SAXParserFactory factory = SAXParserFactory.newInstance();
+	
+	private URL obrURL;
+	private File obrlCache;
+	private File bundleCache;
+	private long updatePeriod;
+	
+	public AbstractOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+		super(id);
+		this.obrURL = repositoryURL;
+		this.obrlCache = obrCache;
+		this.bundleCache = bundleCache;
+		this.updatePeriod = updatePeriod;
+	}
+	
+	public void refresh() {
+		obrlCache.delete();
+	}
+	
+	protected void readBundles(OBRListener listener) throws Exception {
+		syncOBRIndex();		
+		OBRHandler handler = new OBRHandler(getObrURL(), getBundleCache(), listener);
+		SAXParser parser = factory.newSAXParser();
+		parser.parse(getObrlCache(), handler );
+	}
+	
+	private void syncOBRIndex() {
+		if ( isUpdated() ) {
+			InputStream in = null;
+			OutputStream out = null;
+			
+			try {
+				URLConnection c = getObrURL().openConnection();
+				c.connect();
+				in = c.getInputStream();
+				out = new FileOutputStream(getObrlCache());
+				stream(in, out);
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+				getObrlCache().setLastModified(0);
+			}
+			finally {
+				close(in, out);
+			}
+		}		
+	}
+
+	private void close(InputStream in, OutputStream out) {
+		if ( in != null ) {
+			try {
+				in.close();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		if ( out != null ) {
+			try {
+				out.close();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+	}
+
+	private void stream(InputStream in, OutputStream out) throws IOException {
+		byte[] buf = new byte[1024];
+		for(;;) {
+			int r = in.read(buf);
+			if ( r == -1 ) {
+				break;
+			}
+			out.write(buf, 0, r);
+		}
+		out.flush();
+	}
+
+	private boolean isUpdated() {
+		if ( !getObrlCache().exists() ) {
+			return true;
+		}
+		
+		return getObrlCache().lastModified() + getUpdatePeriod() < System.currentTimeMillis();
+	}
+
+	public URL getObrURL() {
+		return obrURL;
+	}
+
+	public File getObrlCache() {
+		return obrlCache;
+	}
+
+	public File getBundleCache() {
+		return bundleCache;
+	}
+
+	public long getUpdatePeriod() {
+		return updatePeriod;
+	}
+
+
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java
new file mode 100644
index 0000000..f0ed825
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.lang.ref.SoftReference;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+public class CachingOBRBundleRepository extends AbstractOBRBundleRepository {
+
+	private SoftReference<List<ISigilBundle>> bundles;
+	
+	public CachingOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+		super(id, repositoryURL, obrCache, bundleCache, updatePeriod);
+	}
+	
+	@Override
+	public void accept(IRepositoryVisitor visitor, int options) {
+		for ( ISigilBundle b : loadFromCache(options) ) {
+			if ( !visitor.visit(b) ) {
+				break;
+			}
+		}
+	}
+	
+	public synchronized void refresh() {
+		super.refresh();
+		if ( bundles != null ) {
+			bundles.clear();
+			notifyChange();
+		}
+	}
+
+	private synchronized List<ISigilBundle> loadFromCache(int options) {
+		List<ISigilBundle> cached = bundles == null ? null : bundles.get();
+		if ( cached == null ) {
+			try {
+				final LinkedList<ISigilBundle> read = new LinkedList<ISigilBundle>();
+				readBundles(new OBRListener() {
+					public void handleBundle(ISigilBundle bundle) {
+						read.add(bundle);
+					}
+				});
+				cached = read;
+				bundles = new SoftReference<List<ISigilBundle>>(cached);
+			} catch (Exception e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			} 
+		}
+		
+		return cached;
+	}
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java
new file mode 100644
index 0000000..4c11840
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.URL;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+public class NonCachingOBRBundleRepository extends AbstractOBRBundleRepository {
+
+	public static void main(String[] args) throws Exception {
+		String url = args[0];
+		String obr = args[1];
+		String cache = args[2];
+		String update = args[3];
+		BldCore.init();
+		NonCachingOBRBundleRepository rep = new NonCachingOBRBundleRepository( "main", new URL(url), new File(obr), new File(cache), Long.parseLong(update));
+		rep.accept(new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				System.out.println( "Found " + bundle );
+				return true;
+			}
+		});
+	}
+	
+	public NonCachingOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+		super(id, repositoryURL, obrCache, bundleCache, updatePeriod);
+	}
+
+	@Override
+	public void accept(final IRepositoryVisitor visitor, int options) {
+		try {
+			readBundles(new OBRListener() {
+				boolean visit = true;
+				public void handleBundle(ISigilBundle bundle) {
+					if ( visit ) {
+						visit = visitor.visit(bundle);
+					}
+				}
+			});
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java
new file mode 100644
index 0000000..b090521
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.LDAPExpr;
+import org.cauldron.sigil.model.common.LDAPParseException;
+import org.cauldron.sigil.model.common.LDAPParser;
+import org.cauldron.sigil.model.common.SimpleTerm;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Version;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+final class OBRHandler extends DefaultHandler {
+	private static final String PACKAGE = "package";
+	private static final String URI = "uri";
+	private static final String PRESENTATION_NAME = "presentationname";
+	private static final String VERSION = "version";
+	private static final String SYMBOLIC_NAME = "symbolicname";
+	private final File cacheDir;
+	private final URL obrURL;
+	private final OBRListener listener;
+	
+	private HashSet<String> sanity = new HashSet<String>();
+	private Locator locator;
+	private ISigilBundle bundle;
+	private IPackageExport export;
+	
+	public OBRHandler(URL obrURL, File bundleCache, OBRListener listener) {
+		this.obrURL = obrURL;
+		this.cacheDir = bundleCache;
+		this.listener = listener;
+	}
+	
+    public void setDocumentLocator (Locator locator) {
+    	this.locator = locator;
+    }
+
+	public void startElement (String uri, String localName,
+			String qName, Attributes attributes) throws SAXException {
+		if ( "resource".equals( qName ) ) {
+			startResource(attributes);
+		}
+		else if ( "capability".equals( qName ) ) {
+			startCapability(attributes);
+		}
+		else if( "require".equals( qName ) ) {
+			startRequire(attributes);
+		}
+		else if ( "p".equals( qName ) ) {
+			startProperty(attributes);
+		}
+	}
+
+	public void endElement (String uri, String localName, String qName)
+	throws SAXException {
+		if ( "resource".equals( qName ) ) {
+			endResource();
+		}
+		else if ( "capability".equals( qName ) ) {
+			endCapability();
+		}
+		else if( "require".equals( qName ) ) {
+			endRequire();
+		}
+		else if ( "p".equals( qName ) ) {
+			endProperty();
+		}
+	}
+
+	private void startResource(Attributes attributes) throws SAXException {
+		try {
+			String uri = attributes.getValue("", URI);
+			if ( uri.endsWith( ".jar" ) ) {
+				if ( !sanity.add(uri) ) {
+					throw new RuntimeException(uri);
+				}
+				ISigilBundle b = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+				IBundleModelElement info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class);
+				info.setSymbolicName(attributes.getValue("", SYMBOLIC_NAME));
+				info.setVersion(new Version(attributes.getValue("", VERSION)));
+				info.setName(attributes.getValue("", PRESENTATION_NAME));
+				URI	l = makeAbsolute(uri);
+				info.setUpdateLocation(l);
+				b.setLocation(cachePath(info));
+				b.setBundleInfo(info);
+				bundle = b;
+			}
+		}
+		catch (Exception e) {
+			throw new SAXParseException("Failed to build bundle info", locator, e);
+		}
+	}
+
+	private URI makeAbsolute(String uri) throws URISyntaxException {
+		URI l = new URI(uri);
+		if ( !l.isAbsolute() ) {
+			String base = obrURL.toExternalForm();
+			int i = base.lastIndexOf("/");
+			if ( i != -1 ) {
+				base = base.substring(0, i);
+				l = new URI( base + (uri.startsWith("/") ? "" : "/") + uri );
+			}
+		}
+		return l;
+	}
+
+	private IPath cachePath(IBundleModelElement info) {
+		return new Path(cacheDir.getAbsolutePath()).append( info.getSymbolicName() + "_" + info.getVersion() + ".jar" );
+	}
+
+	private void startCapability(Attributes attributes) {
+		if ( bundle != null ) {
+			if ( PACKAGE.equals( attributes.getValue("", "name") ) ) {
+				export = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+			}
+		}
+	}
+
+	private void startRequire(Attributes attributes) throws SAXParseException {
+		if ( bundle != null ) {
+			String name = attributes.getValue("name");
+			if ( PACKAGE.equals( name ) ) {
+				IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+				try {
+					LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") );
+					pi.setPackageName(decodePackage(expr, locator));
+					pi.setVersions(decodeVersions(expr, locator));
+					pi.setOptional(Boolean.valueOf(attributes.getValue("optional")));
+					bundle.getBundleInfo().addImport(pi);
+				} catch (LDAPParseException e) {
+					throw new SAXParseException("Failed to parse filter", locator, e);
+				}
+			}
+			else if ( "bundle".equals( name ) ) {
+				IRequiredBundle b = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+				try {
+					LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") );
+					b.setSymbolicName(decodeSymbolicName(expr, locator));
+					b.setVersions(decodeVersions(expr, locator));
+					b.setOptional(Boolean.valueOf(attributes.getValue("optional")));
+					bundle.getBundleInfo().addRequiredBundle(b);
+				} catch (Exception e) {
+					System.err.println( "Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName() );
+					throw new SAXParseException("Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName(), locator, e);
+				}
+			}
+			//else if ( "ee".equals( name ) ) {
+				// TODO ignore for now...
+			//}
+			//else if ( "service".equals( name ) ) {
+				// TODO ignore for now...
+			//}
+			//else {
+			//	for ( int i = 0; i < attributes.getLength(); i++ ) {
+			//		System.out.println( "Found requirement " + attributes.getValue(i) );				
+			//	}
+			//}
+		}
+	}
+
+	private static VersionRange decodeVersions(LDAPExpr expr, Locator locator) throws SAXParseException {
+		try {
+			return VersionRangeHelper.decodeVersions(expr);			
+		}
+		catch (NumberFormatException e) {
+			throw new SAXParseException(e.getMessage(), locator);
+		}
+	}
+
+	private void startProperty(Attributes attributes) {
+		if ( export != null ) {
+			String name = attributes.getValue("", "n");
+			String value = attributes.getValue("", "v");
+			if ( PACKAGE.equals( name ) ) {
+				export.setPackageName(value);
+			}
+			else if ( "uses".equals( name ) ) {
+				String[] split = value.split(",");
+				export.setUses( Arrays.asList(split) );
+			}
+			else if ( "version".equals( name ) ) {
+				export.setVersion( new Version(value) );
+			}
+		}
+	}
+
+	private void endResource() {
+		if ( bundle != null ) {
+			listener.handleBundle(bundle);
+			bundle = null;
+		}
+	}
+
+	private void endCapability() {
+		if ( bundle != null ) {
+			if ( export != null ) {
+				bundle.getBundleInfo().addExport(export);
+				export = null;
+			}
+		}
+	}
+
+	private void endRequire() {
+		// TODO Auto-generated method stub
+		
+	}
+
+	private void endProperty() {
+		// TODO Auto-generated method stub
+		
+	}
+
+	private static String decodePackage(LDAPExpr expr, Locator locator) throws SAXParseException {
+		ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1);
+		
+		findTerms("package", expr, terms);
+		
+		if ( terms.isEmpty() ) {
+			throw new SAXParseException("Missing name filter in " + expr, locator);
+		}
+		
+		return terms.get(0).getRval();
+	}
+	
+	private static String decodeSymbolicName(LDAPExpr expr, Locator locator) throws SAXParseException {
+		ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1);
+		
+		findTerms("symbolicname", expr, terms);
+		
+		if ( terms.isEmpty() ) {
+			throw new SAXParseException("Missing name filter in " + expr, locator);
+		}
+		
+		return terms.get(0).getRval();
+	}
+	
+	private static void findTerms(String string, LDAPExpr expr, List<SimpleTerm> terms) throws SAXParseException {
+		if ( expr instanceof SimpleTerm ) {
+			SimpleTerm term = (SimpleTerm) expr;
+			if ( term.getName().equals(string) ) {
+				terms.add(term);
+			}
+		}
+		else {
+			for ( LDAPExpr c : expr.getChildren() ) {
+				findTerms(string, c, terms);
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java
new file mode 100644
index 0000000..ae7bdd2
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface OBRListener {
+	void handleBundle(ISigilBundle bundle);
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java
new file mode 100644
index 0000000..4653d38
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+
+public class OBRRepositoryProvider implements IRepositoryProvider {
+	public IBundleRepository createRepository(String id, Properties preferences) throws RepositoryException {
+		try {
+			URL repositoryURL = new URL( preferences.getProperty("url") );
+			File indexCache = new File( preferences.getProperty("index") );
+			File localCache = new File( preferences.getProperty("cache") );
+			// TODO create user configurable updatePeriod
+			long updatePeriod = TimeUnit.MILLISECONDS.convert(60*60*24*7, TimeUnit.SECONDS);
+			if ( preferences.getProperty("inmemory") == null ) {
+				return new NonCachingOBRBundleRepository(id, repositoryURL, indexCache, localCache, updatePeriod);
+			}
+			else {
+				return new CachingOBRBundleRepository(id, repositoryURL, indexCache, localCache, updatePeriod);
+			}
+		}
+		catch (MalformedURLException e) {
+			throw new RepositoryException("Invalid repository url", e);
+		}
+	}
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java
new file mode 100644
index 0000000..50b0fb1
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.model.common.LDAPExpr;
+import org.cauldron.sigil.model.common.LDAPParseException;
+import org.cauldron.sigil.model.common.LDAPParser;
+import org.cauldron.sigil.model.common.Not;
+import org.cauldron.sigil.model.common.Ops;
+import org.cauldron.sigil.model.common.SimpleTerm;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.osgi.framework.Version;
+
+class VersionRangeHelper {
+	
+	// e.g. (&(version>=1.0.0)(version<=2.0.0)) (&(version>1.0.0)(version<2.0.0)) (&(!(version<1.0.0))(!(version>2.0.0))) (&(!(version<=1.0.0))(!(version>=2.0.0))) (version=1.0.0) (version>=1.0.0) (version<=2.0.0) (version>1.0.0) (version<2.0.0)  (!(version>2.0.0)) (!(version<1.0.0)) (!(version>=2.0.0)) (!(version<=1.0.0))
+	public static void main(String[] args) throws LDAPParseException {
+		for ( String arg : args ) {
+			LDAPExpr expr = LDAPParser.parseExpression(arg.trim());
+			System.out.println( expr + " -> " + decodeVersions(expr) ); 
+		}
+	}
+	
+	static VersionRange decodeVersions(LDAPExpr expr) throws NumberFormatException {
+		ArrayList<LDAPExpr> terms = new ArrayList<LDAPExpr>(1);
+		
+		findExpr("version", expr, terms);
+		
+		if ( terms.isEmpty() ) {
+			// woo hoo!
+			return VersionRange.ANY_VERSION;
+		}
+		else {
+			switch ( terms.size() ) {
+			case 1: {
+				return parseSimpleVersionRange(terms.get(0));
+			}
+			case 2: {
+				return parseCompoundVersionRange(terms.get(0), terms.get(1));
+			}
+			default: {
+				// (&(version>=min)(!(version=min))(version<=max)(!(version=max))) 	- (min,max) - not dealt with!!
+				// (&(|(version>min)(version=min))(|(version<max)(version=max))) 	- [min,max] - not dealt with!!
+				throw new NumberFormatException("Failed to parse complicated version expression " + expr);				
+			}
+			}
+		}
+	}
+
+	// (&(version>=min)(version<=max)) 									- [min,max]
+	// (&(version>min)(version<max))									- (min,max)
+	//
+	// (&(!(version<min))(!(version>max)))								- [min,max]
+	// (&(!(version<=min))(!(version>=max)) 							- (min,max)
+	private static VersionRange parseCompoundVersionRange(LDAPExpr left, LDAPExpr right) throws NumberFormatException {
+		VersionRange one = parseSimpleVersionRange(left);
+		VersionRange two = parseSimpleVersionRange(right);
+		
+		// sanity check
+		if ( one.isPointVersion() || two.isPointVersion() ) {
+			throw new NumberFormatException("Unexpected point version in compound expression " + left);				
+		}
+		
+		VersionRange max = one.getFloor().equals( Version.emptyVersion ) ? one : two;
+		VersionRange min = max == one ? two : one;
+
+		return new VersionRange( min.isOpenFloor(), min.getFloor(), max.getCeiling(), max.isOpenCeiling() );
+	}
+
+	// possible variations				
+	// (version=v)														- [v,v]
+	//
+	// (version>=min)													- [min,*)
+	// (version<=max)													- [0,max]
+	//
+	// (version>min)													- (min,*)
+	// (version<max)													- [0,max)
+	//
+	// (!(version>max))													- [0,max]
+	// (!(version<min))													- [min,*)
+	// (!(version>=max))												- [0,max)
+	// (!(version<=min))												- (0,*)
+	private static VersionRange parseSimpleVersionRange(LDAPExpr expr) throws NumberFormatException {
+		Version min = Version.emptyVersion;
+		Version max = VersionRange.INFINITE_VERSION;
+		boolean openFloor = false;
+		boolean openCeiling = false;
+		if ( expr instanceof Not ) {
+			Not n = (Not) expr;
+			SimpleTerm t = (SimpleTerm) n.getEx();
+			if ( t.getOp() == Ops.EQ ) {
+				throw new NumberFormatException("Unexpected point version in negated expression " + expr);								
+			}
+			if ( !isMax(t.getOp()) ) {
+				max = toVersion(t);
+				openCeiling = !openFloor(t);
+			}
+			else if ( !isMin(t.getOp()) ) {
+				min = toVersion(t);
+				openFloor = !openCeiling(t);
+			}
+			else {
+				throw new IllegalStateException("Unexpected operator " + t.getOp());
+			}
+		}
+		else {
+			SimpleTerm t = (SimpleTerm) expr;
+			if ( t.getOp().equals( Ops.EQ ) ) {
+				max = toVersion(t);
+				min = max;
+				openFloor = false;
+				openCeiling = false;
+			}
+			else if ( isMax(t.getOp()) ) {
+				max = toVersion(t); 
+				openCeiling = openCeiling(t);
+			}
+			else if ( isMin(t.getOp()) ) {
+				min = toVersion(t);
+				openFloor = openFloor(t);
+			}
+			else {
+				throw new IllegalStateException("Unexpected operator " + t.getOp());
+			}
+		}
+
+		return new VersionRange( openFloor, min, max, openCeiling );
+	}
+
+	private static Version toVersion(SimpleTerm t) {
+		return new Version(t.getRval());
+	}
+
+	private static boolean isMax(Ops op) {
+		return op == Ops.LE || op == Ops.LT;
+	}
+	
+	private static boolean isMin(Ops op) {
+		return op == Ops.GE || op == Ops.GT;
+	}
+
+	private static boolean openFloor(SimpleTerm t) {
+		return t.getOp() == Ops.GT;
+	}
+
+	private static boolean openCeiling(SimpleTerm t) {
+		return t.getOp() == Ops.LT;
+	}
+	
+	private static void findExpr(String string, LDAPExpr expr, List<LDAPExpr> terms) {
+		if ( expr instanceof SimpleTerm ) {
+			SimpleTerm term = (SimpleTerm) expr;
+			if ( term.getName().equals(string) ) {
+				terms.add(term);
+			}
+		}
+		else if ( expr instanceof Not ) {
+			Not not = (Not) expr;
+			if ( not.getEx() instanceof SimpleTerm ) {
+				SimpleTerm term = (SimpleTerm) not.getEx();
+				if ( term.getName().equals(string) ) {
+					terms.add(not);
+				}
+			}
+		}
+		else {
+			for ( LDAPExpr c : expr.getChildren() ) {
+				findExpr(string, c, terms);
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.all.feature/.project b/sigil/org.cauldron.sigil.all.feature/.project
new file mode 100644
index 0000000..91235d4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.all.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.all.feature/build.properties b/sigil/org.cauldron.sigil.all.feature/build.properties
new file mode 100644
index 0000000..df096f5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/build.properties
@@ -0,0 +1,2 @@
+# Setting bin.includes to empty ensures no feature JAR is built
+# bin.includes=
diff --git a/sigil/org.cauldron.sigil.all.feature/feature.xml b/sigil/org.cauldron.sigil.all.feature/feature.xml
new file mode 100644
index 0000000..a486371
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/feature.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<feature
+      id="org.cauldron.sigil.all.feature"
+      label="Sigil All Feature"
+      version="0.8.0">
+
+   <description url="http://www.example.com/description">
+      [Enter Feature Description here.]
+   </description>
+
+   <copyright url="http://www.apache.org/licenses/LICENSE-2.0">
+      [Enter Feature Description here.]
+   </copyright>
+
+   <license url="http://www.apache.org/licenses/LICENSE-2.0">
+      Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+   </license>
+
+   <includes
+         id="org.cauldron.sigil.feature"
+         version="0.0.0"/>
+
+   <includes
+         id="org.cauldron.sigil.obr.feature"
+         version="0.0.0"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.core/.classpath b/sigil/org.cauldron.sigil.core/.classpath
new file mode 100644
index 0000000..e26ebfb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.classpath
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins">
+		<accessrules>
+			<accessrule kind="accessible" pattern="org/cauldron/newton/descriptor/**"/>
+			<accessrule kind="accessible" pattern="org/eclipse/pde/internal/**"/>
+		</accessrules>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry exported="true" kind="lib" path="lib/commons-lang-2.4.jar" sourcepath="org.cauldron.sigil.coresrc.zip"/>
+	<classpathentry kind="output" path="build/classes"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.core/.project b/sigil/org.cauldron.sigil.core/.project
new file mode 100644
index 0000000..ed75ac8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.core</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ed4b35e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Tue Sep 16 15:53:19 BST 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f9bcf63
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF
@@ -0,0 +1,28 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.cauldron.sigil.core
+Bundle-SymbolicName: org.cauldron.sigil.core;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.SigilCore
+Bundle-Vendor: Paremus Ltd.
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.expressions,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.core,
+ org.eclipse.ui,
+ org.eclipse.ui.console;bundle-version="3.3.0",
+ org.cauldron.sigil.utils,
+ org.cauldron.bld.core
+Bundle-ActivationPolicy: lazy
+Export-Package: org.cauldron.sigil,
+ org.cauldron.sigil.install,
+ org.cauldron.sigil.job,
+ org.cauldron.sigil.model.project,
+ org.cauldron.sigil.model.repository,
+ org.cauldron.sigil.model.util,
+ org.cauldron.sigil.preferences
+Bundle-ClassPath: .,
+ lib/commons-lang-2.4.jar
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: junit.framework;version="3.8.2";resolution:=optional
diff --git a/sigil/org.cauldron.sigil.core/build.properties b/sigil/org.cauldron.sigil.core/build.properties
new file mode 100644
index 0000000..a462907
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/build.properties
@@ -0,0 +1,10 @@
+source.. = src/
+output.. = build/classes/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               lib/commons-lang-2.4.jar,\
+               schema/
+bin.excludes = META-INF/.svn/
+src.includes = src/,\
+               META-INF/
diff --git a/sigil/org.cauldron.sigil.core/plugin.xml b/sigil/org.cauldron.sigil.core/plugin.xml
new file mode 100644
index 0000000..3128c58
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/plugin.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+   <extension-point id="org.cauldron.sigil.repositoryprovider" name="Bundle Repository Provider" schema="schema/org.cauldron.sigil.repositoryprovider.exsd"/>
+   <extension-point id="org.cauldron.sigil.installbuilder" name="Newton Install Builder" schema="schema/org.cauldron.sigil.runtime.installbuilder.exsd"/>
+   
+   <extension
+         point="org.eclipse.core.contenttype.contentTypes">
+      <content-type
+            base-type="org.eclipse.core.runtime.text"
+            file-extensions="script,nsh"
+            id="org.cauldron.sigil.content.NewtonScriptType"
+            name="Newton Script"
+            priority="normal">
+      </content-type>
+
+   </extension>
+   <extension
+         point="org.eclipse.core.expressions.propertyTesters">
+      <propertyTester
+            class="org.cauldron.sigil.property.SigilPropertyTester"
+            id="org.cauldron.sigil.property.NewtonPropertyTester"
+            namespace="org.cauldron.sigil"
+            properties="isNewtonResource,isCompositeResource,isSystemResource,isScriptResource,isSigilProject"
+            type="org.eclipse.core.resources.IResource"/>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.adapters">
+      <factory
+            adaptableType="org.eclipse.core.resources.IFile"
+            class="org.cauldron.sigil.internal.adapter.FileAdaptorFactory">
+         <adapter type="org.cauldron.sigil.model.eclipse.ISigilBundle"/>
+      </factory>
+      <factory
+            adaptableType="org.eclipse.core.resources.IProject"
+            class="org.cauldron.sigil.internal.adapter.ProjectAdaptorFactory">
+         <adapter type="org.cauldron.sigil.model.project.ISigilProjectModel"/>
+      </factory>
+   </extension>
+   <extension
+         id="org.cauldron.sigil.core.sigilnature"
+         name="Sigil Nature"
+         point="org.eclipse.core.resources.natures">
+      <runtime>
+         <run class="org.cauldron.sigil.nature.SigilProjectNature"/>
+      </runtime>
+   </extension>
+   <extension
+   		 id="sigilBuilder"
+         name="Sigil Bundle Builder"
+         point="org.eclipse.core.resources.builders">
+      <builder
+            hasNature="false"
+            isConfigurable="false">
+         <run class="org.cauldron.sigil.internal.builders.SigilIncrementalProjectBuilder"/>
+      </builder>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer
+            class="org.cauldron.sigil.preferences.SigilPreferencesInitializer">
+      </initializer>
+   </extension>
+   <extension
+         point="org.cauldron.sigil.repositoryprovider">
+      <provider
+            class="org.cauldron.sigil.internal.repository.eclipse.WorkspaceRepositoryProvider"
+            defaultLevel="-3"
+            dynamic="false"
+            id="org.cauldron.sigil.core.workspaceprovider"
+            type="Workspace Repository">
+      </provider>
+      <provider
+            class="org.cauldron.bld.core.repository.FileSystemRepositoryProvider"
+            dynamic="true"
+            id="org.cauldron.sigil.core.file"
+            type="File System Repository">
+      </provider>
+   </extension>
+   <extension
+         id="org.cauldron.sigil.unresolvedDependencyMarker"
+         name="Unresolved Dependency"
+         point="org.eclipse.core.resources.markers">
+      <persistent
+            value="true">
+      </persistent>
+      <super
+            type="org.eclipse.core.resources.problemmarker">
+      </super>
+      <attribute
+            name="element">
+      </attribute>
+      <attribute
+            name="versionRange">
+      </attribute>
+   </extension>
+   <extension
+         id="org.cauldron.sigil.unresolvedDependencyMarker.importPackage"
+         name="Unresolved Import Package"
+         point="org.eclipse.core.resources.markers">
+      <persistent
+            value="true">
+      </persistent>
+      <super
+            type="org.cauldron.sigil.unresolvedDependencyMarker">
+      </super>
+   </extension>
+   <extension
+         id="org.cauldron.sigil.unresolvedDependencyMarker.requireBundle"
+         name="Unresolve Require Bundle"
+         point="org.eclipse.core.resources.markers">
+      <persistent
+            value="true">
+      </persistent>
+      <super
+            type="org.cauldron.sigil.unresolvedDependencyMarker">
+      </super>
+   </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd
new file mode 100644
index 0000000..051c866
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd
@@ -0,0 +1,158 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appinfo>
+         <meta.schema plugin="org.cauldron.sigil.core" id="org.cauldron.sigil.repository" name="Sigil Bundle Repository Provider"/>
+      </appinfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appinfo>
+            <meta.element />
+         </appinfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="provider" minOccurs="0" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute translatable="true"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="provider">
+      <complexType>
+         <attribute name="id" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="type" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="java" basedOn=":org.cauldron.sigil.repository.IRepositoryProvider"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+         <attribute name="icon" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="resource"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+         <attribute name="dynamic" type="boolean" use="default" value="true">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="defaultLevel" type="string" use="default" value="500">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="since"/>
+      </appinfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="examples"/>
+      </appinfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="apiinfo"/>
+      </appinfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="implementation"/>
+      </appinfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd
new file mode 100644
index 0000000..3522aa4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.runtime" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appinfo>
+         <meta.schema plugin="org.cauldron.sigil.runtime" id="org.cauldron.sigil.runtime.installbuilder" name="Newton Install Builder"/>
+      </appinfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appinfo>
+            <meta.element />
+         </appinfo>
+      </annotation>
+      <complexType>
+         <sequence>
+            <element ref="builder" minOccurs="0" maxOccurs="unbounded"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute translatable="true"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="builder">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="java" basedOn=":org.cauldron.sigil.install.INewtonInstallBuilder"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+         <attribute name="priority" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="since"/>
+      </appinfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="examples"/>
+      </appinfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="apiinfo"/>
+      </appinfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="implementation"/>
+      </appinfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java
new file mode 100644
index 0000000..b0e123e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java
@@ -0,0 +1,570 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.install.IOSGiInstallManager;
+import org.cauldron.sigil.internal.install.OSGiInstallManager;
+import org.cauldron.sigil.internal.model.project.SigilModelRoot;
+import org.cauldron.sigil.internal.model.project.SigilProject;
+import org.cauldron.sigil.internal.model.repository.RepositoryConfiguration;
+import org.cauldron.sigil.internal.repository.eclipse.GlobalRepositoryManager;
+import org.cauldron.sigil.internal.repository.eclipse.SigilRepositoryManager;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilModelRoot;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilCore extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.core";
+    public static final String NATURE_ID = PLUGIN_ID + ".sigilnature";
+    public static final String PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.SigilPreferencePage";
+    public static final String OSGI_INSTALLS_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.osgiInstalls";
+    public static final String EXCLUDED_RESOURCES_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.excludedResources";
+	public static final String REPOSITORIES_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.repositoriesPreferencePage";
+    public static final String OSGI_SCRIPT_TYPE = "org.cauldron.sigil.content.OSGiScriptType";
+    public static final String SIGIL_PROJECT_FILE = IBldProject.PROJECT_FILE;
+	public static final String BUILDER_ID = PLUGIN_ID + ".sigilBuilder";
+	public static final String CLASSPATH_CONTAINER_PATH = PLUGIN_ID + ".classpathContainer";
+
+    public static final String OSGI_INSTALLS = "org.cauldron.osgi.installs";
+	public static final String OSGI_DEFAULT_INSTALL_ID = "org.cauldron.osgi.default.install.id";
+	public static final String OSGI_INSTALL_PREFIX = "org.cauldron.osgi.install.";
+	public static final String OSGI_SOURCE_LOCATION = "org.cauldron.osgi.source.location";
+	public static final String OSGI_INSTALL_CHECK_PREFERENCE = "org.cauldron.osgi.install.check";
+	public static final String LIBRARY_KEYS_PREF = "org.cauldron.osgi.library.keys";
+	public static final String PREFERENCES_REBUILD_PROJECTS = "org.cauldron.sigil.rebuild.projects";
+	public static final String QUALIFY_VERSIONS = "org.cauldron.sigil.qualify.versions";
+	
+	public static final String DEFAULT_VERSION_LOWER_BOUND = "org.cauldron.sigil.versionLowerBound";
+	public static final String DEFAULT_VERSION_UPPER_BOUND = "org.cauldron.sigil.versionUpperBound";
+
+	public static final String DEFAULT_EXCLUDED_RESOURCES = "org.cauldron.sigil.excludedResources";
+	public static final String PREFERENCES_NOASK_OSGI_INSTALL = "org.cauldron.sigil.noAskOSGIHome";
+	public static final String PREFERENCES_ADD_IMPORT_FOR_EXPORT = "org.cauldron.sigil.addImportForExport";
+	public static final String INCLUDE_OPTIONAL_DEPENDENCIES = "org.cauldron.sigil.includeOptionalDependencies";
+
+	public static final String INSTALL_BUILDER_EXTENSION_POINT_ID = "org.cauldron.sigil.installbuilder";
+	public static final String REPOSITORY_PROVIDER_EXTENSION_POINT_ID = "org.cauldron.sigil.repositoryprovider";
+	
+	public static final String MARKER_UNRESOLVED_DEPENDENCY = "org.cauldron.sigil.unresolvedDependencyMarker";
+	public static final String MARKER_UNRESOLVED_IMPORT_PACKAGE = "org.cauldron.sigil.unresolvedDependencyMarker.importPackage";
+	public static final String MARKER_UNRESOLVED_REQUIRE_BUNDLE = "org.cauldron.sigil.unresolvedDependencyMarker.requireBundle";
+	public static final String REPOSITORY_SET = PLUGIN_ID + ".repository.set";
+	public static final String PREFERENCES_INCLUDE_OPTIONAL = PLUGIN_ID + ".include.optional";
+	
+	private static final Object NULL = new Object();
+
+	// The shared instance
+	private static SigilCore plugin;
+
+	private ServiceTracker descriptorTracker;
+	private ServiceTracker registryTracker;
+	private ServiceTracker serializerTracker;
+
+	private static IRepositoryConfiguration repositoryConfig;
+	private static OSGiInstallManager installs;
+	private static ISigilModelRoot modelRoot;
+	private static HashMap<Object, SigilRepositoryManager> repositoryManagers = new HashMap<Object, SigilRepositoryManager>();
+	private static GlobalRepositoryManager globalRepositoryManager;
+
+	/**
+	 * Returns the shared instance
+	 * 
+	 * @return the shared instance
+	 */
+	public static SigilCore getDefault() {
+		return plugin;
+	}
+
+	public static CoreException newCoreException(String msg, Throwable t) {
+		return new CoreException(makeStatus(msg, t, IStatus.ERROR));
+	}
+
+	public static void log(String msg) {
+		DebugPlugin.log(makeStatus(msg, null, IStatus.INFO));
+	}
+
+	public static void error(String msg) {
+		DebugPlugin.log(makeStatus(msg, null, IStatus.ERROR));
+	}
+
+	public static void error(String msg, Throwable t) {
+		DebugPlugin.log(makeStatus(msg, t, IStatus.ERROR));
+	}
+
+	public static void warn(String msg) {
+		DebugPlugin.log(makeStatus(msg, null, IStatus.WARNING));
+	}
+
+	public static void warn(String msg, Throwable t) {
+		DebugPlugin.log(makeStatus(msg, t, IStatus.WARNING));
+	}
+	
+	private static IStatus makeStatus(String msg, Throwable t, int status) {
+		if (t instanceof CoreException) {
+			CoreException c = (CoreException) t;
+			return c.getStatus();
+		} else {
+			return new Status(status, SigilCore.PLUGIN_ID, status, msg, t);
+		}
+	}
+
+	public static boolean isSigilProject(IProject resource) {
+		if ( resource == null ) return false;
+		
+		if ( resource.isAccessible() && resource instanceof IProject ) {
+			IProject project = (IProject) resource;
+			try {
+				return project.hasNature(NATURE_ID);
+			} catch (CoreException e) {
+				error( e.getMessage(), e );
+				return false;
+			}
+		}
+		else {
+			return false;
+		}
+	}
+	
+	public static boolean hasProjectNature(IProject project)
+			throws CoreException {
+		return project.getNature(NATURE_ID) != null;
+	}
+
+	public static ResourceBundle getResourceBundle() {
+		return ResourceBundle.getBundle("resources."
+				+ SigilCore.class.getName(), Locale.getDefault(),
+				SigilCore.class.getClassLoader());
+	}
+
+	public static ISigilProjectModel create(IProject project)
+			throws CoreException {
+		if (project.hasNature(NATURE_ID)) {
+			return new SigilProject(project);
+		} else {
+			throw newCoreException("Project " + project.getName()
+					+ " is not a sigil project", null);
+		}
+	}
+
+	/**
+	 * The constructor
+	 */
+	public SigilCore() {
+		plugin = this;
+	}
+
+	public void earlyStartup() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+	 * )
+	 */
+	public void start(final BundleContext context) throws Exception {
+		super.start(context);
+
+		modelRoot = new SigilModelRoot();
+		
+		repositoryConfig = new RepositoryConfiguration();
+
+		installs = new OSGiInstallManager();
+		
+		globalRepositoryManager = new GlobalRepositoryManager();
+
+		registerModelElements(context);
+		registerResourceListeners();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+	 * )
+	 */
+	public void stop(BundleContext context) throws Exception {
+		if (descriptorTracker != null) {
+			descriptorTracker.close();
+			descriptorTracker = null;
+		}
+
+		if (registryTracker != null) {
+			registryTracker.close();
+			registryTracker = null;
+		}
+
+		if (serializerTracker != null) {
+			serializerTracker.close();
+			serializerTracker = null;
+		}
+		
+		for ( SigilRepositoryManager m : repositoryManagers.values() ) {
+			m.destroy();
+		}
+		
+		repositoryManagers.clear();
+		
+		globalRepositoryManager.destroy();
+		globalRepositoryManager = null;
+
+		plugin = null;
+
+		super.stop(context);
+	}
+
+
+	public static boolean isBundledPath(String bp) throws CoreException {
+		boolean bundle = JavaHelper.isCachedBundle(bp);
+
+		if (!bundle) {
+			bundle = isProjectPath(bp);
+
+			if (!bundle) {
+				for (IBundleRepository r : getGlobalRepositoryManager().getRepositories()) {
+					bundle = isBundlePath(bp, r);
+					if (bundle)
+						break;
+				}
+			}
+		}
+
+		return bundle;
+	}
+
+	private static boolean isBundlePath(final String bp, IBundleRepository r) {
+		final AtomicBoolean flag = new AtomicBoolean();
+
+		IRepositoryVisitor visitor = new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle b) {
+				IPath path = b.getLocation();
+				if (path != null && path.toOSString().equals(bp)) {
+					flag.set(true);
+					return false;
+				} else {
+					return true;
+				}
+			}
+
+		};
+
+		r.accept(visitor, ResolutionConfig.INDEXED_ONLY
+				| ResolutionConfig.LOCAL_ONLY);
+
+		return flag.get();
+	}
+
+	private static boolean isProjectPath(String bp) throws CoreException {
+		for (ISigilProjectModel p : SigilCore.getRoot().getProjects()) {
+			IPath path = p.findOutputLocation();
+			
+			if (path.toOSString().equals(bp)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+	
+	private void registerResourceListeners() {
+		final IResourceChangeListener listener = new IResourceChangeListener() {
+			public void resourceChanged(IResourceChangeEvent event) {
+				IResourceDelta delta = event.getDelta();
+				if ( delta != null ) {
+					try {
+						delta.accept(new IResourceDeltaVisitor() {
+							public boolean visit(IResourceDelta delta) throws CoreException {
+								IResource resource = delta.getResource();
+								if(resource instanceof IProject) {
+									IProject project = (IProject) resource;
+									if ( SigilCore.isSigilProject(project) ) {
+										switch (delta.getKind()) {
+										case IResourceDelta.REMOVED:
+										case IResourceDelta.ADDED:
+											rebuildAllBundleDependencies(Job.getJobManager().createProgressGroup());
+											break;
+										}									
+									}
+									// Recurse no more
+									return false;
+								}							
+								return true;
+							}
+						});
+					} catch (CoreException e) {
+						error("Failed to update after change", e);
+					}
+				}
+			}
+		};
+		
+		Job job = new Job("Initialising sigil resource listeners") {
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
+				return Status.OK_STATUS;
+			}
+		};
+		job.setSystem(true);
+		job.schedule();
+	}
+
+	private void registerModelElements(BundleContext context) {
+		// trick to get eclipse to lazy load BldCore for model elements
+		BldCore.getLicenseManager();
+		String uri = "http://sigil.codecauldron.org/xml/sigil-namespace";
+		ModelElementFactory.getInstance().register(ISigilProjectModel.class,
+				SigilProject.class, "project", "sigil", uri);
+	}
+
+	public static IOSGiInstallManager getInstallManager() {
+		return installs;
+	}
+
+	public static ISigilModelRoot getRoot() {
+		return modelRoot;
+	}
+	
+	public static IRepositoryManager getGlobalRepositoryManager() {
+		return globalRepositoryManager;
+	}
+
+	public static IRepositoryManager getRepositoryManager(String set) {
+		SigilRepositoryManager manager = null;
+		
+		if ( set == null ) {
+			manager = repositoryManagers.get( NULL );
+			if ( manager == null ) {
+				manager = new SigilRepositoryManager(null);
+				manager.initialise();
+				repositoryManagers.put( NULL, manager );
+			}
+		}
+		else {
+			manager = repositoryManagers.get(set);
+			
+			if ( manager == null ) {
+				manager = new SigilRepositoryManager(set);
+				manager.initialise();
+				repositoryManagers.put( set, manager );
+			}			
+		}
+		
+		return manager;
+	}
+	public static IRepositoryManager getRepositoryManager(ISigilProjectModel model) {
+		return getRepositoryManager(loadProjectRepositorySet(model));
+	}
+	
+	private static String loadProjectRepositorySet(ISigilProjectModel model) {
+		if ( model == null ) {
+			return null;
+		}
+		
+		return model.getPreferences().get(REPOSITORY_SET, null );
+	}		
+
+
+	public static IRepositoryConfiguration getRepositoryConfiguration() {
+		return repositoryConfig;
+	}	
+
+	public static void rebuildAllBundleDependencies(IProgressMonitor monitor) {
+		Collection<ISigilProjectModel> projects = getRoot().getProjects();
+		if ( !projects.isEmpty() ) {
+			SubMonitor progress = SubMonitor.convert(monitor, projects.size()*20);
+			for ( ISigilProjectModel p : projects ) {
+				rebuild(p, progress);
+			}			
+		}
+		
+		monitor.done();
+	}
+	
+	public static void rebuildBundleDependencies(ISigilProjectModel project, IProgressMonitor monitor) {
+		HashSet<ISigilProjectModel> affected = new HashSet<ISigilProjectModel>(project.findDependentProjects(monitor));
+		affected.add(project);
+
+		SubMonitor progress = SubMonitor.convert(monitor, affected.size()*20);
+		for (ISigilProjectModel dependent : affected) {
+			rebuild(dependent, progress);
+		}
+	}	
+	
+	
+	private static void rebuild(ISigilProjectModel dependent, SubMonitor progress) {
+		try {
+			dependent.resetClasspath(progress.newChild(10));
+			dependent.getProject().build(IncrementalProjectBuilder.FULL_BUILD, progress.newChild(10));
+		} catch (CoreException e) {
+			SigilCore.error("Failed to rebuild " + dependent, e);
+		}
+	}
+
+	public IPath findDefaultBundleLocation(ISigilProjectModel m)
+			throws CoreException {
+		IPath loc = m.getProject().getLocation();
+		loc = loc.append(m.getJavaModel().getOutputLocation().removeFirstSegments(1));
+		loc = loc.removeLastSegments(1).append( "lib" );
+		return loc.append(m.getSymbolicName() + ".jar");
+	}
+
+	public static void makeSigilProject(IProject project,
+			IProgressMonitor monitor) throws CoreException {
+		IProjectDescription description = project.getDescription();
+
+		String[] natures = description.getNatureIds();
+		String[] newNatures = new String[natures.length + 1];
+		System.arraycopy(natures, 0, newNatures, 0, natures.length);
+		newNatures[natures.length] = SigilCore.NATURE_ID;
+		description.setNatureIds(newNatures);
+
+		ICommand sigilBuild = description.newCommand();
+		sigilBuild.setBuilderName(SigilCore.BUILDER_ID);
+
+		ICommand javaBuild = description.newCommand();
+		javaBuild.setBuilderName(JavaCore.BUILDER_ID);
+
+		description.setBuildSpec(new ICommand[] { javaBuild, sigilBuild });
+
+		project.setDescription(description, new SubProgressMonitor(monitor, 2));
+
+		IJavaProject java = JavaCore.create(project);
+		if (java.exists()) {
+			IClasspathEntry[] cp = java.getRawClasspath();
+			// XXX fix for http://jira.codecauldron.org/browse/SIGIL-304
+			if ( !isSigilOnClasspath(cp) ) {
+				ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(
+						Arrays.asList(cp));
+				entries.add(JavaCore.newContainerEntry(new Path(
+						SigilCore.CLASSPATH_CONTAINER_PATH)));
+				java.setRawClasspath(entries.toArray(new IClasspathEntry[entries
+						.size()]), monitor);
+			}
+		}
+	}
+
+	/**
+	 * @param cp
+	 * @return
+	 */
+	private static boolean isSigilOnClasspath(IClasspathEntry[] cp) {
+		for ( IClasspathEntry e : cp ) {
+			if ( e.getEntryKind() == IClasspathEntry.CPE_CONTAINER && e.getPath().segment(0).equals(SigilCore.CLASSPATH_CONTAINER_PATH) ) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static Image loadImage(URL url) throws IOException {
+		ImageRegistry registry = getDefault().getImageRegistry();
+		
+		String key = url.toExternalForm();
+		Image img = registry.get( key );
+		
+		if ( img == null ) {
+			img = openImage(url);
+			registry.put( key, img );
+		}
+		
+		return img;
+	}
+
+	private static Image openImage(URL url) throws IOException {
+		Display display = Display.getCurrent();
+		if ( display == null ) {
+			display = Display.getDefault();
+		}
+		
+		InputStream in = null;
+		try {
+			in = url.openStream();
+			return new Image(display, in);
+		}
+		finally {
+			if ( in != null ) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					error( "Failed to close stream", e );
+				}
+			}
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java
new file mode 100644
index 0000000..3e91ca1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Encapsulates all information about a particular OSGi install.
+ * 
+ * @author dave
+ *
+ */
+public interface IOSGiInstall {
+	
+	/**
+	 * A unique id which can be used to refer to this install within the eclipse runtime.
+	 * @return
+	 */
+	String getId();
+	
+	/**
+	 * Where this install is located
+	 * @return
+	 */
+	IPath getInstallLocation();
+
+	/**
+	 * @return
+	 */
+	Map<String, String> getProperties();
+	
+	/**
+	 * @return
+	 */
+	String[] getLaunchArguments();	
+	
+	/**
+	 * @return
+	 */
+	IPath getVarDirectory();
+	
+	/**
+	 * @return
+	 */
+	IOSGiInstallType getType();
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java
new file mode 100644
index 0000000..ce066ea
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+
+public interface IOSGiInstallBuilder {
+	IOSGiInstall build(String id, IPath path) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java
new file mode 100644
index 0000000..c33f746
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+public interface IOSGiInstallManager {
+	IOSGiInstall findInstall(String id);
+	String[] getInstallIDs();
+	IOSGiInstall[] getInstalls();
+	IOSGiInstall getDefaultInstall();
+	IOSGiInstallType findInstallType(String location);
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java
new file mode 100644
index 0000000..f975151
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.swt.graphics.Image;
+
+//import org.eclipse.swt.graphics.Image;
+
+public interface IOSGiInstallType {
+	/**
+	 * @return
+	 */
+	String getName();
+
+	/**
+	 * 
+	 * @return
+	 */
+	String getVersion();
+	
+	/**
+	 * @return
+	 */
+	String getMainClass();
+	
+	/**
+	 * @return
+	 */
+	String[] getClassPath();
+
+	/**
+	 * @return
+	 */
+	IPath getSourceLocation();
+	
+	/**
+	 * @return
+	 */
+	IPath getJavaDocLocation();
+	
+	/**
+	 * Return the paths of any bundles that are started by default in this OSGi instance.
+	 * @return
+	 */
+	IPath[] getDefaultBundleLocations();
+
+	String getId();
+
+	Image getIcon();
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java
new file mode 100644
index 0000000..633f1f3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+
+public class OSGiInstall implements IOSGiInstall {
+
+	private String id;
+	private IPath installLocation;
+	private String[] launchArgs;
+	private Map<String, String> properties;
+	private IPath varDirectory;
+	private IOSGiInstallType type;
+	
+	public IPath getVarDirectory() {
+		return varDirectory;
+	}
+
+	public void setVarDirectory(IPath varDirectory) {
+		this.varDirectory = varDirectory;
+	}
+
+	public OSGiInstall(String id) {
+		this.id = id;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public IPath getInstallLocation() {
+		return installLocation;
+	}
+
+	public void setInstallLocation(IPath installLocation) {
+		this.installLocation = installLocation;
+	}
+
+	public String[] getLaunchArguments() {
+		return launchArgs;
+	}
+	
+	public void setLaunchArguments(String[] launchArgs) {
+		this.launchArgs = launchArgs;
+	}
+
+	public Map<String, String> getProperties() {
+		return properties;
+	}
+	
+	public void setProperties(Map<String,String> properties) {
+		this.properties = properties;
+	}
+
+	public String toString() {
+		return "OSGiInstall[\n" + 
+			"id=" + id + "\n" +
+			"type=" + type + "\n" +
+			"installLocation=" + installLocation + "\n" +
+			"launchArgs=" + Arrays.asList(launchArgs) + "\n" +
+			"properties=" + properties + "\n" +
+			"]";
+	}
+
+        public IOSGiInstallType getType() {
+                return type;
+        }
+        
+        public void setType(IOSGiInstallType type) {
+                this.type = type;
+        }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java
new file mode 100644
index 0000000..e584f1c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.swt.graphics.Image;
+
+public class OSGiInstallType implements IOSGiInstallType {
+
+	private String id;
+	private String name;
+	private String version;
+	private String mainClass;
+	private String[] classPath;
+	private IPath javaDocLocation;
+	private IPath sourceLocation;
+	private IPath[] defaultBundleLocations;
+	private Image icon;
+	
+	public Image getIcon() {
+		return icon;
+	}
+
+	public void setIcon(Image icon) {
+		this.icon = icon;
+	}
+
+	public String getId() {
+		return id;
+	}
+	
+	public String[] getClassPath() {
+		return classPath;
+	}
+
+	public IPath[] getDefaultBundleLocations() {
+		return defaultBundleLocations;
+	}
+
+	public IPath getJavaDocLocation() {
+		return javaDocLocation;
+	}
+
+	public String getMainClass() {
+		return mainClass;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public IPath getSourceLocation() {
+		return sourceLocation;
+	}
+
+	public String getVersion() {
+		return version;
+	}
+	
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setVersion(String version) {
+		this.version = version;
+	}
+
+	public void setMainClass(String mainClass) {
+		this.mainClass = mainClass;
+	}
+
+	public void setClassPath(String[] classPath) {
+		this.classPath = classPath;
+	}
+
+	public void setJavaDocLocation(IPath javaDocLocation) {
+		this.javaDocLocation = javaDocLocation;
+	}
+
+	public void setSourceLocation(IPath sourceLocation) {
+		this.sourceLocation = sourceLocation;
+	}
+
+	public void setDefaultBundleLocations(IPath[] defaultBundleLocations) {
+		this.defaultBundleLocations = defaultBundleLocations;
+	}
+	
+	public String toString() {
+		return "OSGiInstallType[\n" + 
+			"name=" + name + "\n" +
+			"version=" + version + "\n" +
+			"mainClass=" + mainClass + "\n" +
+			"classPath=" + Arrays.asList(classPath) + "\n" +
+			"javaDocLocation=" + javaDocLocation + "\n" +
+			"sourceLocation=" + sourceLocation + "\n" +
+			"defaultBundleLocations=" + Arrays.asList(defaultBundleLocations) + "\n" +
+			"]";
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java
new file mode 100644
index 0000000..4b63ad3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.adapter;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterFactory;
+
+/**
+ * @author savage
+ *
+ */
+public class FileAdaptorFactory implements IAdapterFactory {
+
+	public FileAdaptorFactory() {
+		
+	}
+	private Class<?>[] types = new Class<?>[] { ISigilBundle.class };
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Object getAdapter( Object adaptableObject, Class adapterType ) {
+        Object adapted = null;
+        
+        IFile file = (IFile) adaptableObject;
+        
+        if ( ISigilBundle.class.equals( adapterType ) ) {
+        	adapted = adaptBundle(file);
+        }
+		
+		return adapted;
+	}
+    
+    private Object adaptBundle(IFile file) {
+        Object adapted = null;
+        IProject project = file.getProject();
+        try {
+            if ( SigilCore.hasProjectNature( project ) ) {
+                ISigilProjectModel sigil = SigilCore.create( project );
+                ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+                bundle.setParent( sigil );
+                adapted = bundle;
+            }
+        }
+        catch ( CoreException e ) {
+			SigilCore.error( "Failed to construct bundle", e );
+        } catch (ModelElementFactoryException e) {
+			SigilCore.error( "Failed to construct bundle", e );
+		}
+        
+        return adapted;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+	 */
+	@SuppressWarnings("unchecked")
+	public Class[] getAdapterList() {
+		return types;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java
new file mode 100644
index 0000000..4811476
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.adapter;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterFactory;
+
+/**
+ * @author savage
+ *
+ */
+public class ProjectAdaptorFactory implements IAdapterFactory {
+
+	private Class<?>[] types = new Class<?>[] { ISigilProjectModel.class };
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+	 */
+	@SuppressWarnings("unchecked")
+	public Object getAdapter( Object adaptableObject, Class adapterType ) {
+        Object adapted = null;
+        
+        IProject project = (IProject) adaptableObject;
+        
+		if ( ISigilProjectModel.class.equals( adapterType ) ) {
+			adapted = adaptProject(project);
+		}
+		
+		return adapted;
+	}
+    
+    private Object adaptProject(IProject project) {
+    	try {
+			if ( SigilCore.isSigilProject(project) ) {
+				return SigilCore.create(project);
+			}
+		} catch (CoreException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+	 */
+	@SuppressWarnings("unchecked")
+	public Class[] getAdapterList() {
+		return types;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java
new file mode 100644
index 0000000..5ac8117
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.builders;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class BuildConsole extends MessageConsole {
+
+	private static final ImageDescriptor imageDescriptor = null;
+	private MessageConsoleStream stream;
+
+	public BuildConsole() {
+		super("Sigil Build", imageDescriptor, true);
+	}
+
+	public synchronized MessageConsoleStream getMessageStream() {
+		if ( stream == null ) {
+			stream = newMessageStream();
+		}
+		return stream;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java
new file mode 100644
index 0000000..acea59a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.builders;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaModelMarker;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class SigilIncrementalProjectBuilder extends IncrementalProjectBuilder {
+
+	@Override
+	protected IProject[] build(int kind, @SuppressWarnings("unchecked")Map args, IProgressMonitor monitor)
+			throws CoreException {
+		IProject project = getProject();
+		
+		if ( checkOk( project ) ) {
+			switch ( kind ) {
+			case CLEAN_BUILD:
+			case FULL_BUILD:
+				fullBuild( project, monitor );
+				break;
+			case AUTO_BUILD:
+			case INCREMENTAL_BUILD:
+				autoBuild( project, monitor );
+				break;
+			}
+		}
+        
+		return null;
+	}
+
+    /**
+	 * @param install
+	 * @param project
+	 * @param monitor
+     * @throws CoreException 
+	 */
+	private void autoBuild(IProject project,
+			IProgressMonitor monitor) throws CoreException {
+		IResourceDelta delta = getDelta(project);
+		final boolean[] changed = new boolean[1];
+		ISigilProjectModel sigil = SigilCore.create(project);
+		final IPath bldRoot = sigil.findBundleLocation().removeLastSegments(1);
+		
+		delta.accept(new IResourceDeltaVisitor() {
+			public boolean visit(IResourceDelta delta) throws CoreException {
+				if ( !changed[0] ) {
+					IResource res = delta.getResource();
+					if ( res.getType() == IResource.FILE ) {
+						changed[0] = !bldRoot.isPrefixOf(res.getLocation());
+					}
+				}
+				return !changed[0];
+			}			
+		});
+		
+		if ( changed[0] ) {
+			doBuild(project, monitor);
+		}
+	}
+
+	/**
+	 * @param install
+	 * @param project
+	 * @param monitor
+	 * @throws CoreException 
+	 */
+	private void fullBuild(IProject project,
+			IProgressMonitor monitor) throws CoreException {
+		doBuild(project, monitor);
+	}
+
+	private boolean checkOk(IProject project) throws CoreException {
+		IMarker[] markers = project.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
+		
+		for ( IMarker m : markers ) {
+			Integer s = (Integer) m.getAttribute(IMarker.SEVERITY);
+			if ( s != null && s.equals( IMarker.SEVERITY_ERROR ) ) {
+				SigilCore.log( "Skipping " + project.getName() + " build due to unresolved errors" );
+				return false;
+			}
+		}
+		
+		return true;
+	}
+
+	private void doBuild(IProject project, IProgressMonitor monitor ) throws CoreException {
+		ISigilProjectModel sigil = SigilCore.create(project);
+		IBldProject bld = sigil.getBldProject();
+
+		File[] classpath = buildClasspath(sigil, monitor);
+		
+		String destPattern = buildDestPattern(sigil);
+		
+		Properties env = new Properties();
+		
+		BundleBuilder bb = new BundleBuilder(bld, classpath, destPattern, env);
+
+    	for (IBldProject.IBldBundle bundle : bld.getBundles()) {
+    		String id = bundle.getId();
+    		loginfo("creating bundle: " + id);
+    		int nWarn = 0;
+    		int nErr = 0;
+    		String msg = "";
+
+    		try {
+    			boolean modified = bb.createBundle(bundle, false, new BundleBuilder.Log() {
+    				public void warn(String msg) {
+    					logwarn(msg);
+    				}
+    				public void verbose(String msg) {
+    					loginfo(msg);
+    				}
+    			});
+    			nWarn = bb.warnings().size();
+    			if (!modified) {
+    				msg = " (not modified)";
+    			}
+    		} catch (Exception e) {
+    			List<String> errors = bb.errors();
+    			if (errors != null) {
+    				nErr = errors.size();
+    				for (String err : errors) {
+    					logerror(err);
+    				}
+    			}
+    			throw SigilCore.newCoreException("Failed to create: " + id + ": " + e, e);
+    		} finally {
+    			loginfo(id + ": " + count(nErr, "error") + ", " + count(nWarn, "warning") + msg);
+    		}
+    	}
+	}
+    
+	private static void loginfo(String message) {
+		BuildConsole console = findConsole();
+		MessageConsoleStream stream = console.getMessageStream();
+		stream.println("INFO: " + message);
+	}
+
+	private static void logwarn(String message) {
+		BuildConsole console = findConsole();
+		MessageConsoleStream stream = console.getMessageStream();
+		stream.println("WARN: " + message);
+	}
+	
+	private static void logerror(String message) {
+		BuildConsole console = findConsole();
+		MessageConsoleStream stream = console.getMessageStream();
+		stream.println("ERROR: " + message);
+	}
+	
+	private static BuildConsole findConsole() {
+		BuildConsole console = null;
+		
+        IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager();
+        
+        for ( IConsole c : manager.getConsoles() ) {
+        	if ( c instanceof BuildConsole ) {
+        		console = (BuildConsole) c;
+        		break;
+        	}
+        }
+        
+        if ( console == null ) {
+        	console = new BuildConsole();
+        	manager.addConsoles( new IConsole[] { console } );
+        }
+        
+        return console;
+	}
+
+	private String buildDestPattern(ISigilProjectModel sigil) throws CoreException {
+		IPath loc = sigil.findBundleLocation().removeLastSegments(1);
+		
+		loc.toFile().mkdirs();
+		
+		return loc.toOSString() + File.separator + "[name].jar";
+	}
+
+	private File[] buildClasspath(ISigilProjectModel sigil, IProgressMonitor monitor) throws CoreException {
+		ArrayList<File> files = new ArrayList<File>();
+		
+		buildLocalClasspath(sigil, files);
+		buildExternalClasspath(sigil, files, monitor);
+			
+		return files.toArray( new File[files.size()] );
+	}
+
+	private void buildExternalClasspath(ISigilProjectModel sigil,
+			ArrayList<File> files, IProgressMonitor monitor) throws CoreException {
+		Collection<IClasspathEntry> entries = sigil.findExternalClasspath(monitor);
+		files.ensureCapacity(files.size() + entries.size());
+		
+		for ( IClasspathEntry cp : entries ) {
+			convert(cp, sigil, files);
+		}
+	}
+
+	private void buildLocalClasspath(ISigilProjectModel sigil, ArrayList<File> files) throws CoreException {
+		Collection<IClasspathEntry> entries = JavaHelper.findClasspathEntries(sigil.getBundle());
+		files.ensureCapacity(files.size() + entries.size());
+		for ( IClasspathEntry cp : entries ) {
+			convert(cp, sigil, files);
+		}
+		
+		if ( !sigil.getBundle().getComposites().isEmpty() ) {
+			throw new IllegalStateException("XXX-FIXME-XXX");
+		}
+	}
+
+	private void convert(IClasspathEntry cp, ISigilProjectModel sigil, ArrayList<File> files) throws CoreException {
+		switch( cp.getEntryKind() ) {
+		case IClasspathEntry.CPE_PROJECT: {
+			IProject p = findProject(cp.getPath());
+			ISigilProjectModel project = SigilCore.create(p);
+			for ( String scp : project.getBundle().getClasspathEntrys() ) {
+				IClasspathEntry jcp = project.getJavaModel().decodeClasspathEntry(scp);
+				convert( jcp, project, files );
+			}
+			break;
+		}
+		case IClasspathEntry.CPE_SOURCE : {
+			IPath path = cp.getOutputLocation() == null ? sigil.getJavaModel().getOutputLocation() : cp.getOutputLocation();
+			IFolder buildFolder = sigil.getProject().getFolder(path.removeFirstSegments(1));
+			if ( buildFolder.exists() ) {
+				files.add(buildFolder.getLocation().toFile());
+			}
+			break;
+		}
+		case IClasspathEntry.CPE_LIBRARY: {
+			IPath p = cp.getPath();
+			
+			IPath ppath = sigil.getProject().getFullPath();
+			
+			if ( ppath.isPrefixOf(p) ) {
+				p = sigil.getProject().getLocation().append( p.removeFirstSegments(1) );
+			}
+			
+			files.add( p.toFile() );
+			break;
+		}
+		case IClasspathEntry.CPE_VARIABLE:
+			cp = JavaCore.getResolvedClasspathEntry(cp);
+			if ( cp != null ) {
+				IPath p = cp.getPath();
+				files.add( p.toFile() );
+			}
+			break;
+		}
+	}
+
+	private IProject findProject(IPath path) throws CoreException {
+		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+		for ( IProject p : root.getProjects() ) {
+			IPath projectPath = p.getFullPath();
+			if ( projectPath.equals( path ) ) {
+				return p;
+			}
+		}
+		
+		throw SigilCore.newCoreException("No such project " + path, null);
+	}
+
+	private String count(int count, String msg) {
+		return count + " " + msg + (count == 1 ? "" : "s");
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java
new file mode 100644
index 0000000..ce5f20f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.install;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.install.IOSGiInstallBuilder;
+import org.cauldron.sigil.install.IOSGiInstallManager;
+import org.cauldron.sigil.install.IOSGiInstallType;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+
+public class OSGiInstallManager implements IOSGiInstallManager, IPropertyChangeListener {
+	private static final int NORMAL_PRIORITY = 0;
+	
+	private LinkedList<IOSGiInstallBuilder> builders = new LinkedList<IOSGiInstallBuilder>();
+
+	private HashMap<IPath, IOSGiInstall> pathToinstall = new HashMap<IPath, IOSGiInstall>();
+	private HashMap<String, IOSGiInstall> idToInstall = new HashMap<String, IOSGiInstall>();
+	
+	private String defaultId;
+	
+	private boolean initialised;
+		
+	public IOSGiInstall findInstall(String id) {
+		init();
+		return idToInstall.get(id);
+	}
+
+	public String[] getInstallIDs() {
+		init();
+		return idToInstall.keySet().toArray( new String[idToInstall.size()] );
+	}
+
+	public IOSGiInstall[] getInstalls() {
+		init();
+		return idToInstall.values().toArray( new IOSGiInstall[idToInstall.size()] );
+	}
+
+	public IOSGiInstall getDefaultInstall() {
+		init();
+		return findInstall(defaultId);
+	}
+
+	public IOSGiInstallType findInstallType(String location) {
+		IOSGiInstallType type = null;
+		
+		try {
+			IOSGiInstall install = buildInstall("tmp", new Path( location ) );
+			type = install == null ? null : install.getType();
+		} catch (CoreException e) {
+			SigilCore.error( "Failed to build install", e);
+		}
+		
+		return type;
+	}
+	
+	public void propertyChange(PropertyChangeEvent event) {
+		synchronized( this ) {
+			if ( event.getProperty().equals(SigilCore.OSGI_INSTALLS) ) {
+				clearInstalls();
+				String val = (String) event.getNewValue();
+				addInstalls(val);
+			}
+			else if ( event.getProperty().equals( SigilCore.OSGI_DEFAULT_INSTALL_ID ) ) {
+				defaultId = (String) event.getNewValue();
+			}
+		}
+	}
+
+	private void init() {
+		boolean show = false;
+		
+		IPreferenceStore prefs = getPreferenceStore(); 
+		
+		synchronized( this ) {
+			if ( !initialised ) {
+				initialised = true;
+				
+				prefs.addPropertyChangeListener(this);
+				
+				String val = prefs.getString(SigilCore.OSGI_INSTALLS);
+				
+				boolean noAsk = prefs.getBoolean(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL);
+				if(val == null || val.trim().length() == 0) {
+					show = !noAsk;
+				}
+				else {
+					addInstalls(val);
+					defaultId = prefs.getString(SigilCore.OSGI_DEFAULT_INSTALL_ID);
+				}
+			}
+		}
+		
+		if ( show ) {
+			showInstallPrefs(prefs);
+		}		
+	}
+	
+	private void addInstalls(String prop) {
+		if ( prop != null && prop.trim().length() > 0 ) {
+			IPreferenceStore prefs = getPreferenceStore();
+			
+			for (String id : prop.split(",")) {
+				String path = prefs.getString( SigilCore.OSGI_INSTALL_PREFIX + id );
+				addInstall(id, new Path( path ) );
+			}
+		}				
+	}
+
+	private IPreferenceStore getPreferenceStore() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+
+	private void showInstallPrefs(final IPreferenceStore prefs) {
+		Runnable r = new Runnable() {
+			public void run() {
+				MessageDialogWithToggle questionDialog = MessageDialogWithToggle.openYesNoQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Sigil Configuration", "Missing OSGi installation. Open preferences to configure it now?", "Do not show this message again", false, null, null);
+				prefs.setValue(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL, questionDialog.getToggleState());
+				if(questionDialog.getReturnCode() == IDialogConstants.YES_ID) {
+					PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, SigilCore.OSGI_INSTALLS_PREFERENCES_ID, null, null);
+					dialog.open();
+				}
+			}
+		};
+		Display d = Display.getCurrent();
+		if ( d == null ) {
+			d = Display.getDefault();
+			d.asyncExec(r);
+		}
+		else {
+			d.syncExec(r);
+		}
+	}
+
+	private IOSGiInstall addInstall(String id, IPath path) {
+		IOSGiInstall install = pathToinstall.get(path);
+		
+		if ( install == null ) {
+			try {
+				install = buildInstall(id, path);
+				if ( install != null ) {
+					pathToinstall.put( path, install );
+					idToInstall.put( install.getId(), install );
+				}
+			}
+			catch (CoreException e) {
+				SigilCore.error( "Failed to build install for " + path, e);
+			}
+		}
+		
+		return install;
+	}
+	
+	private IOSGiInstall buildInstall(String id, IPath path) throws CoreException {
+		initBuilders();
+		IOSGiInstall install = null;
+		
+		for ( IOSGiInstallBuilder b : builders ) {
+			install = b.build(id, path);
+
+			if ( install != null ) {
+				break;
+			}
+		}
+		
+		return install;
+	}
+
+	private void clearInstalls() {
+		idToInstall.clear();
+		pathToinstall.clear();
+	}
+
+	private void initBuilders() {
+		synchronized( builders ) {
+			if ( builders.isEmpty() ) {
+				final HashMap<IOSGiInstallBuilder, Integer> tmp = new HashMap<IOSGiInstallBuilder, Integer>();
+				
+				IExtensionRegistry registry = Platform.getExtensionRegistry();
+				IExtensionPoint p = registry.getExtensionPoint(SigilCore.INSTALL_BUILDER_EXTENSION_POINT_ID);
+				for ( IExtension e : p.getExtensions() ) {
+					for ( IConfigurationElement c : e.getConfigurationElements() ) {
+						createBuilderFromElement(c, tmp);
+					}
+				}
+				
+				builders = new LinkedList<IOSGiInstallBuilder>(tmp.keySet());
+				Collections.sort(builders, new Comparator<IOSGiInstallBuilder>() {
+					public int compare(IOSGiInstallBuilder o1, IOSGiInstallBuilder o2) {
+						int p1 = tmp.get(o1);
+						int p2 = tmp.get(o2);
+						
+						if ( p1 == p2 ) {
+							return 0;
+						}
+						else if ( p1 > p2 ) {
+							return -1;
+						}
+						else {
+							return 1;
+						}
+					}
+				});
+			}
+		}
+	}
+
+	private void createBuilderFromElement(IConfigurationElement c, Map<IOSGiInstallBuilder, Integer> builder) {
+		try {
+			IOSGiInstallBuilder b = (IOSGiInstallBuilder) c.createExecutableExtension("class");
+			int priority = parsePriority( c );
+			builder.put(b, priority);
+		} catch (CoreException e) {
+			SigilCore.error("Failed to create builder", e);
+		}
+	}
+	
+	private int parsePriority(IConfigurationElement c) {
+		String str = c.getAttribute("priority");
+		
+		if ( str == null ) {
+			return NORMAL_PRIORITY;
+		}
+		else {
+			return Integer.parseInt(str);
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java
new file mode 100644
index 0000000..ea05121
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.project;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilModelRoot;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class SigilModelRoot implements ISigilModelRoot {
+	public List<ISigilProjectModel> getProjects() {
+		IProject[] all = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+		ArrayList<ISigilProjectModel> projects = new ArrayList<ISigilProjectModel>(all.length);
+		for (IProject p : all) {
+			try {
+				if (p.isOpen() && p.hasNature(SigilCore.NATURE_ID)) {
+					ISigilProjectModel n = SigilCore.create(p);
+					projects.add(n);
+				}
+			} catch (CoreException e) {
+				SigilCore.error("Failed to build model element", e);
+			}
+		}
+
+		return projects;
+	}
+
+	public Collection<ISigilProjectModel> resolveDependentProjects(
+			ISigilProjectModel sigil, IProgressMonitor monitor) {
+		HashSet<ISigilProjectModel> dependents = new HashSet<ISigilProjectModel>();
+
+		for (ISigilProjectModel n : getProjects()) {
+			if (!sigil.equals(n)) {
+				for (IPackageExport pe : sigil.getBundle().getBundleInfo().getExports()) {
+					for (IPackageImport i : n.getBundle().getBundleInfo()
+							.getImports()) {
+						if (pe.getPackageName().equals(i.getPackageName())
+								&& i.getVersions().contains(pe.getVersion())) {
+							dependents.add(n);
+						}
+					}
+
+					for (ILibraryImport l : n.getBundle().getBundleInfo().getLibraryImports()) {
+						ILibrary lib = SigilCore.getRepositoryManager(sigil).resolveLibrary(l);
+
+						if (lib != null) {
+							for (IPackageImport i : lib.getImports()) {
+								if (pe.getPackageName().equals(
+										i.getPackageName())
+										&& i.getVersions().contains(
+												pe.getVersion())) {
+									dependents.add(n);
+								}
+							}
+						} else {
+							SigilCore.error("No library found for " + l);
+						}
+					}
+				}
+
+				for (IRequiredBundle r : n.getBundle().getBundleInfo().getRequiredBundles()) {
+					if (sigil.getSymbolicName().equals(r.getSymbolicName())
+							&& r.getVersions().contains(sigil.getVersion())) {
+						dependents.add(n);
+					}
+				}
+			}
+		}
+
+		return dependents;
+	}
+
+	public Collection<ISigilBundle> resolveBundles(ISigilProjectModel sigil, IModelElement element, boolean includeOptional, IProgressMonitor monitor) throws CoreException {
+		int options = ResolutionConfig.INCLUDE_DEPENDENTS;
+		if ( includeOptional ) {
+			options |= ResolutionConfig.INCLUDE_OPTIONAL;
+		}
+		
+		ResolutionConfig config = new ResolutionConfig(options);
+		try {
+			IBundleResolver resolver = SigilCore.getRepositoryManager(sigil).getBundleResolver();
+			IResolution resolution = resolver.resolve(element, config, new ResolutionMonitorAdapter(monitor));
+			resolution.synchronize(monitor);
+			return resolution.getBundles();
+		} catch (ResolutionException e) {
+			throw SigilCore.newCoreException(e.getMessage(), e);
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java
new file mode 100644
index 0000000..7a7f3af
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.project;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ThreadProgressMonitor;
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IParent;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.osgi.framework.Version;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProject extends AbstractCompoundModelElement implements ISigilProjectModel {
+
+	private static final long serialVersionUID = 1L;
+	
+	private IFile bldProjectFile;
+    private IProject project;
+    private IBldProject bldProject;
+    
+    private ISigilBundle bundle;
+
+	private IEclipsePreferences preferences;
+
+    public SigilProject() {
+    	super( "Sigil Project" );
+    }
+    
+    public SigilProject(IProject project) throws CoreException {
+    	this();
+        this.project = project;
+        bldProjectFile = project.getFile( new Path( SigilCore.SIGIL_PROJECT_FILE) );
+    }
+    
+    // to aid testing conversion between project file formats
+    public InputStream saveBundle(ISigilBundle b) throws CoreException {
+    	setBundle(b);
+    	// FIXME causes NPE in JavaHelper
+    	// calculateUses();
+    	return buildContents();
+    }
+    
+	public void save(IProgressMonitor monitor) throws CoreException {
+		SubMonitor progress = SubMonitor.convert(monitor, 100);
+		
+    	calculateUses();
+    	
+    	bldProjectFile.setContents( buildContents(), IFile.KEEP_HISTORY, progress.newChild(10));
+    	
+		IRepositoryManager manager = SigilCore.getRepositoryManager(this);
+		ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL);
+		
+		try {
+			IResolution res = manager.getBundleResolver().resolve(this, config, new ResolutionMonitorAdapter(progress.newChild(10)));
+			if ( !res.isSynchronized() ) {
+				res.synchronize(progress.newChild(60));
+			}
+		} catch (ResolutionException e) {
+			throw SigilCore.newCoreException("Failed to synchronize dependencies", e);
+		}
+		
+		
+		progress.setWorkRemaining(40);
+		
+    	SigilCore.rebuildBundleDependencies( this, progress.newChild(20) );
+    }
+	
+	/**
+	 * Returns the project custom preference pool.
+	 * Project preferences may include custom encoding.
+	 * @return IEclipsePreferences or <code>null</code> if the project
+	 * 	does not have a java nature.
+	 */
+	public Preferences getPreferences(){
+		synchronized(this) {
+			if ( preferences == null ) {
+				preferences = loadPreferences();
+			}
+			
+			return preferences;
+		}
+	}
+	
+	
+	/**
+	 * @return
+	 */
+	private synchronized IEclipsePreferences loadPreferences() {
+		IScopeContext context = new ProjectScope(getProject());
+		final IEclipsePreferences eclipsePreferences = context.getNode(SigilCore.PLUGIN_ID);
+		
+		// Listen to node removal from parent in order to reset cache
+		INodeChangeListener nodeListener = new IEclipsePreferences.INodeChangeListener() {
+			public void added(IEclipsePreferences.NodeChangeEvent event) {
+				// do nothing
+			}
+			
+			public void removed(IEclipsePreferences.NodeChangeEvent event) {
+				if (event.getChild() == eclipsePreferences) {
+					synchronized( SigilProject.this ) {
+						preferences = null;
+					}
+					((IEclipsePreferences) eclipsePreferences.parent()).removeNodeChangeListener(this);
+				}
+			}
+		};
+
+		((IEclipsePreferences) eclipsePreferences.parent()).addNodeChangeListener(nodeListener);
+		
+		return eclipsePreferences;
+	}
+
+	public Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor) throws CoreException {
+		return JavaHelper.resolveClasspathEntrys(this, monitor);
+	}
+
+	private void calculateUses() {
+		visit( new IModelWalker() {
+			public boolean visit(IModelElement element) {
+				if ( element instanceof IPackageExport ) {
+					IPackageExport pe = (IPackageExport) element;
+					try {
+						pe.setUses( Arrays.asList( JavaHelper.findUses(pe.getPackageName(), SigilProject.this ) ) );
+					} catch (CoreException e) {
+						SigilCore.error( "Failed to build uses list for " + pe, e );
+					}
+				}
+				return true;
+			} 
+		} );
+	}
+
+    public Collection<ISigilProjectModel> findDependentProjects(IProgressMonitor monitor) {
+		return SigilCore.getRoot().resolveDependentProjects(this, monitor);
+	}
+
+	public Version getVersion() {
+		ISigilBundle bundle = getBundle();
+		return bundle == null ? null : bundle.getBundleInfo() == null ? null :  bundle.getBundleInfo().getVersion();
+	}
+
+    public String getSymbolicName() {
+		ISigilBundle bundle = getBundle();
+    	return bundle == null ? null : bundle.getBundleInfo() == null ? null : bundle.getBundleInfo().getSymbolicName();
+	}
+
+    public IProject getProject() {
+        return project;
+    }
+
+	public ISigilBundle getBundle() {
+		if ( bundle == null && bldProjectFile != null ) {
+			synchronized( bldProjectFile ) {
+				try {
+			        if ( bldProjectFile.getLocation().toFile().exists() ) {
+			        	bundle = parseContents(bldProjectFile);
+			        }
+			        else {
+			        	bundle = setupDefaults();
+			        	NullProgressMonitor npm = new NullProgressMonitor();
+			        	bldProjectFile.create( buildContents(), true /* force */, npm);
+			        	project.refreshLocal( IResource.DEPTH_INFINITE, npm );
+			        }
+				} catch (CoreException e) {
+					SigilCore.error( "Failed to build bundle", e);
+				}
+			}
+		}
+		return bundle;
+	}
+	
+	public void setBundle(ISigilBundle bundle) {
+		this.bundle = bundle;
+	}
+	
+	public IJavaProject getJavaModel() {
+		return JavaCore.create( project );
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if ( obj == null ) return false;
+		
+		if ( obj == this ) return true;
+		
+		try {
+			SigilProject p = (SigilProject) obj;
+			return getSymbolicName().equals( p.getSymbolicName() ) && (getVersion() == null ? p.getVersion() == null : getVersion().equals( p.getVersion() ));
+		}
+		catch (ClassCastException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		// TODO Auto-generated method stub
+		return super.hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return "SigilProject[" + getSymbolicName() + ":" + getVersion() + "]";
+	}
+
+	public void resetClasspath(IProgressMonitor monitor) throws CoreException {
+    	Path containerPath = new Path( SigilCore.CLASSPATH_CONTAINER_PATH );
+    	IJavaProject java = getJavaModel();
+		ClasspathContainerInitializer init = JavaCore.getClasspathContainerInitializer(SigilCore.CLASSPATH_CONTAINER_PATH);
+		ThreadProgressMonitor.setProgressMonitor(monitor);
+		try {
+			init.requestClasspathContainerUpdate(containerPath, java, null);
+		}
+		finally {
+			ThreadProgressMonitor.setProgressMonitor(null);
+		}
+	}
+
+	public IPath findBundleLocation() throws CoreException {
+		IPath p = getBundle().getLocation();
+		if ( p == null ) {
+			p = SigilCore.getDefault().findDefaultBundleLocation(this);
+		}
+		return p;
+	}
+
+    public IModelElement findImport(final String packageName, final IProgressMonitor monitor) {
+    	final IModelElement[] found = new IModelElement[1];
+    	
+    	visit( new IModelWalker() {
+			public boolean visit(IModelElement element) {
+				if ( element instanceof IPackageImport ) {
+					IPackageImport pi = (IPackageImport) element;
+					if ( pi.getPackageName().equals( packageName ) ) {
+						found[0] = pi;
+						return false;
+					}
+				}
+				else if ( element instanceof IRequiredBundle ) {
+					IRequiredBundle rb = (IRequiredBundle) element;
+					try {
+						IRepositoryManager manager = SigilCore.getRepositoryManager(SigilProject.this);
+						ResolutionConfig config = new ResolutionConfig(ResolutionConfig.IGNORE_ERRORS);
+						IResolution res = manager.getBundleResolver().resolve(rb, config, new ResolutionMonitorAdapter(monitor));
+						ISigilBundle b = res.getProvider(rb);
+						for ( IPackageExport pe : b.getBundleInfo().getExports() ) {
+							if ( pe.getPackageName().equals( packageName ) ) {
+								found[0] = rb;
+								return false;
+							}
+						}
+					} catch (ResolutionException e) {
+						SigilCore.error( "Failed to resolve " + rb, e );
+					}
+				}
+				return true;
+			}
+    		
+    	});
+    	
+    	return found[0];
+	}
+
+	public boolean isInClasspath(String packageName, IProgressMonitor monitor) throws CoreException {
+		if ( findImport(packageName, monitor) != null ) {
+			return true;
+		}
+		
+		for ( String path : getBundle().getClasspathEntrys() ) {
+			IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
+			for ( IPackageFragmentRoot root : getJavaModel().findPackageFragmentRoots(cp) ) {
+				if ( findPackage( packageName, root ) ) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
+	public boolean isInClasspath(ISigilBundle bundle) {
+		for ( String path : getBundle().getClasspathEntrys() ) {
+			IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
+			switch ( cp.getEntryKind() ) {
+			case IClasspathEntry.CPE_PROJECT:
+				ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class);
+				return p != null && cp.getPath().equals(p.getProject().getFullPath());
+			case IClasspathEntry.CPE_LIBRARY:
+				return cp.getPath().equals(bundle.getLocation());
+			}
+		}	
+		
+		return false;
+	}
+
+	private boolean findPackage(String packageName, IParent parent) throws JavaModelException {
+		for ( IJavaElement e : parent.getChildren() ) {
+			if ( e.getElementType() == IJavaElement.PACKAGE_FRAGMENT ) {
+				return e.getElementName().equals( packageName );
+			}
+			
+			if ( e instanceof IParent ) {
+				if ( findPackage(packageName, (IParent) e) ) {
+					return true;
+				}
+			}
+		}
+		
+		return false;
+	}
+
+	private ISigilBundle setupDefaults() {
+    	ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+    	IBundleModelElement info = ModelElementFactory.getInstance().newModelElement( IBundleModelElement.class );
+    	info.setSymbolicName(project.getName());
+    	bundle.setBundleInfo(info);
+    	bundle.setParent(this);
+    	return bundle;
+    }
+
+	
+	private ISigilBundle parseContents(IFile projectFile) throws CoreException {
+		/*if ( !projectFile.isSynchronized(IResource.DEPTH_ONE) ) {
+			projectFile.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
+		}*/
+		
+		if ( projectFile.getName().equals( SigilCore.SIGIL_PROJECT_FILE) ) {
+			return parseBldContents(projectFile.getLocationURI());
+		}
+		else {
+			throw SigilCore.newCoreException("Unexpected project file: " + projectFile.getName(), null );
+		}
+	}
+	
+    private ISigilBundle parseBldContents(URI uri) throws CoreException {
+		try {
+			bldProject = BldFactory.getProject(uri, true);
+			ISigilBundle bundle = bldProject.getDefaultBundle();
+			bundle.setParent(this);
+			return bundle;
+		} catch (IOException e) {
+			throw SigilCore.newCoreException( "Failed to parse " + uri, e);
+		}
+	}
+
+	private InputStream buildContents() throws CoreException {
+    	ByteArrayOutputStream buf = new ByteArrayOutputStream();    	
+    	try {
+    		if (bldProject == null) {
+            	bldProject = BldFactory.newProject(bldProjectFile.getLocationURI(), null);
+    		}
+        	bldProject.setDefaultBundle(getBundle());
+			bldProject.saveTo(buf);
+		} catch (IOException e) {
+			throw SigilCore.newCoreException("Failed to save project file", e);
+		}
+    	return new ByteArrayInputStream(buf.toByteArray());
+    }
+    
+//    private InputStream buildXMLContents() throws CoreException {
+//    	Serializer serializer = SigilCore.getDefault().getDescriptorSerializer();
+//    	
+//    	ByteArrayOutputStream buf = new ByteArrayOutputStream();
+//    	
+//    	try {
+//    		serializer.serialize(getBundle(), buf);
+//    	} catch (SerializingException e) {
+//			throw SigilCore.newCoreException("Failed to serialize " + this, e);
+//    	}
+//    	
+//        return new ByteArrayInputStream(buf.toByteArray());
+//    }
+
+	public String getName() {
+		return getProject().getName();
+	}
+
+	public IPath findOutputLocation() throws CoreException {
+		return getProject().getLocation().append(
+				getJavaModel().getOutputLocation()
+				.removeFirstSegments(1));
+	}
+
+	public IBldProject getBldProject() throws CoreException {
+		try {
+			return BldFactory.getProject(project.getFile(IBldProject.PROJECT_FILE).getLocationURI());
+		} catch (IOException e) {
+			throw SigilCore.newCoreException("Failed to get project file: ",e);
+		}
+	}
+
+	public boolean isInBundleClasspath(IPackageFragmentRoot root) throws JavaModelException {
+		String enc = getJavaModel().encodeClasspathEntry(root.getRawClasspathEntry());
+		return getBundle().getClasspathEntrys().contains( enc.trim() );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java
new file mode 100644
index 0000000..592e75b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Bundle;
+
+public class RepositoryConfiguration implements IRepositoryConfiguration {
+	
+	private static final String REPOSITORY = "repository.";
+	private static final String REPOSITORY_SET = REPOSITORY + "set.";
+	private static final String REPOSITORY_SETS = REPOSITORY + "sets";
+	private static final String REPOSITORY_TIMESTAMP = REPOSITORY + "timestamp";
+	private static final String INSTANCES = ".instances";
+	private static final String NAME = ".name";
+	private static final String LOC = ".loc";
+	private static final String TIMESTAMP = ".timestamp";
+	
+	public static final String REPOSITORY_DEFAULT_SET = REPOSITORY + "default.set";
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.repository.management.IRepositoryManagement#loadRepositories()
+	 */
+	public List<IRepositoryModel> loadRepositories() {
+		IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+		
+		ArrayList<IRepositoryModel> repositories = new ArrayList<IRepositoryModel>();
+
+		for ( RepositoryType type : loadRepositoryTypes() ) {
+			String typeID = type.getId();
+			
+			if ( type.isDynamic() ) {	
+				String instances = prefs.getString( REPOSITORY + typeID + INSTANCES );
+				if ( instances.trim().length() > 0 ) {
+					for( String instance : instances.split(",") ) {
+						String key = REPOSITORY + typeID + "." + instance;
+						repositories.add( loadRepository(instance, key, type, prefs) );
+					}
+				}
+			}
+			else {
+				String key = REPOSITORY + typeID;
+				repositories.add( loadRepository(typeID, key, type, prefs) );
+			}
+			
+		}
+		
+		return repositories;
+	}
+	
+	public IRepositoryModel findRepository(String id) {
+		for (IRepositoryModel model : loadRepositories()) {
+			if ( model.getId().equals( id ) ) {
+				return model;
+			}
+		}
+		return null;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.repository.management.IRepositoryManagement#saveRepositories(java.util.List)
+	 */
+	public void saveRepositories(List<IRepositoryModel> repositories) throws CoreException {
+		IPreferenceStore prefs = getPreferences();
+		
+		HashMap<IRepositoryType, List<IRepositoryModel>> mapped = new HashMap<IRepositoryType, List<IRepositoryModel>>(repositories.size());
+		
+		saveRepositoryPreferences(repositories, mapped);
+		createNewEntries(mapped, prefs);
+		deleteOldEntries(repositories,prefs);
+		// time stamp is used as a signal to the manager
+		// to update its view of the stored repositories
+		timeStamp(prefs);
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.repository.management.IRepositoryManagement#loadRepositoryTypes()
+	 */
+	public List<RepositoryType> loadRepositoryTypes() {
+		List<RepositoryType> repositories = new ArrayList<RepositoryType>();
+		
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+		
+		IExtensionPoint p = registry.getExtensionPoint(SigilCore.REPOSITORY_PROVIDER_EXTENSION_POINT_ID);
+		
+		for ( IExtension e : p.getExtensions() ) {
+			for ( IConfigurationElement c : e.getConfigurationElements() ) {
+				String id = c.getAttribute("id");
+				String type = c.getAttribute("type");
+				boolean dynamic = Boolean.valueOf( c.getAttribute("dynamic") ); 
+				String icon = c.getAttribute("icon");
+				Image image = (icon == null || icon.trim().length() == 0) ? null : loadImage(e, icon);
+				repositories.add( new RepositoryType(id, type, dynamic, image ) );
+			}
+		}
+		
+		return repositories;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.repository.management.IRepositoryManagement#newRepositoryElement(org.cauldron.sigil.repository.management.RepositoryType)
+	 */
+	public IRepositoryModel newRepositoryElement(IRepositoryType type) {
+		String id = UUID.randomUUID().toString();
+		PreferenceStore prefs = new PreferenceStore();
+		RepositoryModel element = new RepositoryModel(id, "", type, prefs);
+		prefs.setFilename(makeFileName(element));
+		prefs.setValue("id", id);
+		return element;
+	}
+	
+	public IRepositorySet getDefaultRepositorySet() {
+		//int level = findLevel( key + LEVEL, type, prefs );
+		ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+		for ( String s : PrefsUtils.stringToArray(getPreferences().getString( REPOSITORY_DEFAULT_SET)) ) {
+			reps.add( findRepository(s) );
+		}
+		return new RepositorySet( reps );
+	}
+
+	public IRepositorySet getRepositorySet(String name) {
+		String key = REPOSITORY_SET + name;
+		if ( getPreferences().contains(key) ) {
+			ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+			for ( String s : PrefsUtils.stringToArray(getPreferences().getString( key )) ) {
+				reps.add( findRepository(s) );
+			}
+			return new RepositorySet( reps );
+		}
+		else {
+			return null;
+		}
+	}
+
+	public Map<String, IRepositorySet> loadRepositorySets() {
+		IPreferenceStore store = getPreferences();
+		
+		HashMap<String, IRepositorySet> sets = new HashMap<String, IRepositorySet>();
+		
+		for ( String name : PrefsUtils.stringToArray(store.getString(REPOSITORY_SETS))) {
+			String key = REPOSITORY_SET + name;
+			ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+			for ( String s : PrefsUtils.stringToArray(getPreferences().getString( key )) ) {
+				reps.add( findRepository(s) );
+			}
+			sets.put( name, new RepositorySet( reps ) );
+		}
+		
+		return sets;
+	}
+
+	public void saveRepositorySets(Map<String, IRepositorySet> sets) {
+		IPreferenceStore store = getPreferences();
+
+		ArrayList<String> names = new ArrayList<String>();
+		
+		for ( Map.Entry<String, IRepositorySet> set : sets.entrySet() ) {
+			String name = set.getKey();
+			String key = REPOSITORY_SET + name;
+			ArrayList<String> ids = new ArrayList<String>();
+			for ( IRepositoryModel m : set.getValue().getRepositories() ) {
+				ids.add( m.getId() );
+			}
+			store.setValue(key, PrefsUtils.listToString(ids) );
+			names.add( name );
+		}
+		
+		for ( String name : PrefsUtils.stringToArray(store.getString(REPOSITORY_SETS))) {
+			if ( !names.contains(name) ) {
+				String key = REPOSITORY_SET + name;
+				store.setToDefault(key);
+			}
+		}
+		
+		store.setValue(REPOSITORY_SETS, PrefsUtils.listToString(names) );
+		timeStamp(store);
+	}
+			
+	public void setDefaultRepositorySet(IRepositorySet defaultSet) {
+		ArrayList<String> ids = new ArrayList<String>();
+		for ( IRepositoryModel m : defaultSet.getRepositories() ) {
+			ids.add( m.getId() );
+		}
+		IPreferenceStore prefs = getPreferences();
+		prefs.setValue( REPOSITORY_DEFAULT_SET, PrefsUtils.listToString( ids ) );
+		timeStamp(prefs);
+	}
+
+	private void timeStamp(IPreferenceStore prefs) {
+		prefs.setValue( REPOSITORY_TIMESTAMP, System.currentTimeMillis() );
+	}
+
+	private IPreferenceStore getPreferences() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+
+	private void deleteOldEntries(List<IRepositoryModel> repositories, IPreferenceStore prefs) {
+		for ( IRepositoryModel e : loadRepositories() ) {
+			if ( !repositories.contains(e) ) {
+				new File( makeFileName(e) ).delete();
+				String key = makeKey(e);
+				prefs.setToDefault( key + LOC );
+				prefs.setToDefault( key + NAME );
+			}
+		}
+		
+		for ( IRepositoryType type : loadRepositoryTypes() ) {
+			boolean found = false;
+			for ( IRepositoryModel e : repositories ) {
+				if ( e.getType().equals( type ) ) {
+					found = true;
+					break;
+				}
+			}
+			
+			if ( !found ) {
+				prefs.setToDefault( REPOSITORY + type.getId() + INSTANCES );
+			}
+		}
+	}
+
+	private static void createNewEntries(HashMap<IRepositoryType, List<IRepositoryModel>> mapped, IPreferenceStore prefs) {
+		for ( Map.Entry<IRepositoryType, List<IRepositoryModel>> entry : mapped.entrySet() ) {
+			IRepositoryType type = entry.getKey();
+			if ( type.isDynamic() ) {
+				StringBuffer buf = new StringBuffer();
+				
+				for ( IRepositoryModel element : entry.getValue() ) {
+					if ( buf.length() > 0 ) {
+						buf.append( "," );
+					}
+					buf.append( element.getId() );
+					saveRepository(element, prefs);
+				}
+				
+				prefs.setValue( REPOSITORY + type.getId() + INSTANCES, buf.toString() );
+			}
+			else {
+				IRepositoryModel element = entry.getValue().get(0);
+				saveRepository(element, prefs);
+			}
+		}
+	}
+
+	private static void saveRepositoryPreferences(List<IRepositoryModel> repositories,
+			HashMap<IRepositoryType, List<IRepositoryModel>> mapped) throws CoreException {
+		for( IRepositoryModel rep : repositories ) {
+			try {
+				createDir( makeFileName(rep));
+				rep.getPreferences().save();
+				List<IRepositoryModel> list = mapped.get( rep.getType() );
+				if ( list == null ) {
+					list = new ArrayList<IRepositoryModel>(1);
+					mapped.put( rep.getType(), list );
+				}
+				list.add( rep );
+			} catch (IOException e) {
+				throw SigilCore.newCoreException("Failed to save repository preferences", e);
+			}
+		}
+	}
+
+	private static void createDir(String fileName) {
+		File file = new File( fileName );
+		file.getParentFile().mkdirs();
+	}
+
+	private static void saveRepository(IRepositoryModel element, IPreferenceStore prefs) {
+		String key = makeKey(element);
+		prefs.setValue( key + LOC, makeFileName(element) );
+		if ( element.getType().isDynamic() ) {
+			prefs.setValue( key + NAME, element.getName() );
+		}
+		prefs.setValue( key + TIMESTAMP, now() );
+	}
+
+	private static long now() {
+		return System.currentTimeMillis();
+	}
+
+	private static String makeKey(IRepositoryModel element) {
+		IRepositoryType type = element.getType();
+		
+		String key = REPOSITORY + type.getId(); 
+		if ( type.isDynamic() )
+			key = key + "." + element.getId();
+		
+		return key;
+	}
+
+	private static String makeFileName(IRepositoryModel element) {
+		IPath path = SigilCore.getDefault().getStateLocation();
+		path = path.append( "repository" );
+		path = path.append( element.getType().getId() );
+		path = path.append( element.getId() );
+		return path.toOSString();
+	}
+
+	private static RepositoryModel loadRepository(String id, String key, RepositoryType type, IPreferenceStore prefs) {
+		String name = type.isDynamic() ? prefs.getString( key + NAME ) : type.getType();
+		
+		PreferenceStore repPrefs = new PreferenceStore();		
+		RepositoryModel element = new RepositoryModel( id, name, type, repPrefs );
+		
+		String loc = prefs.getString( key + LOC );
+		
+		if ( loc == null || loc.trim().length() == 0 ) {
+			loc = makeFileName(element);
+		}
+		
+		repPrefs.setFilename(loc);
+		
+		if ( new File( loc ).exists() ) {
+			try {
+				repPrefs.load();
+			} catch (IOException e) {
+				SigilCore.error("Failed to load properties for repository " + key, e );
+			}
+		}
+		
+		repPrefs.setValue( "id", id );
+		
+		return element;
+	}
+
+	@SuppressWarnings("unchecked")
+	private static Image loadImage(IExtension ext, String icon) {
+		int i = icon.lastIndexOf( "/" );
+		String path = i == -1 ? "/" : icon.substring(0, i);
+		String name = i == -1 ? icon : icon.substring(i+1);
+		
+		Bundle b = Platform.getBundle(ext.getContributor().getName());
+		
+		Enumeration<URL> en = b.findEntries(path, name, false);
+		Image image = null;
+		
+		if ( en.hasMoreElements() ) {
+			try {
+				image = SigilCore.loadImage(en.nextElement());
+			} catch (IOException e) {
+				SigilCore.error( "Failed to load image", e );
+			}
+		}
+		else {
+			SigilCore.error("No such image " + icon + " in bundle " + b.getSymbolicName() );
+		}
+		
+		return image;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java
new file mode 100644
index 0000000..9b72541
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.eclipse.jface.preference.PreferenceStore;
+
+public class RepositoryModel implements IRepositoryModel {
+	private String id;
+	
+	private String name;
+	
+	private IRepositoryType type;
+	
+	private PreferenceStore preferences;
+		
+	public RepositoryModel(String id, String name, IRepositoryType type, PreferenceStore preferences) {
+		this.id = id;
+		this.name = name;
+		this.type = type;
+		this.preferences = preferences;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getPreferences()
+	 */
+	public PreferenceStore getPreferences() {
+		return preferences;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getType()
+	 */
+	public IRepositoryType getType() {
+		return type;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getId()
+	 */
+	public String getId() {
+		return id;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getName()
+	 */
+	public String getName() {
+		return name;
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.wizard.repository.IRepositoryModel#setName(java.lang.String)
+	 */
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		try {
+			RepositoryModel e = (RepositoryModel) obj;
+			return id.equals(e.id);
+		}
+		catch (ClassCastException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		return id.hashCode();
+	}
+	
+	public String toString() {
+		return type.getId() + ":" + id + ":" + name;
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java
new file mode 100644
index 0000000..30525a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.eclipse.swt.graphics.Image;
+
+public class RepositoryType implements IRepositoryType {
+	private String type;
+	private String id;
+	private Image icon;
+	private boolean dynamic;
+	
+	public RepositoryType(String id, String type, boolean dynamic,
+			Image icon) {
+		this.id = id;
+		this.type = type;
+		this.dynamic = dynamic;
+		this.icon = icon;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getType()
+	 */
+	public String getType() {
+		return type;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getId()
+	 */
+	public String getId() {
+		return id;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getIcon()
+	 */
+	public Image getIcon() {
+		return icon;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#isDynamic()
+	 */
+	public boolean isDynamic() {
+		return dynamic;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		try {
+			RepositoryType t = (RepositoryType) obj;
+			return t.id.equals( id );
+		}
+		catch (ClassCastException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		return id.hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return type;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java
new file mode 100644
index 0000000..c974644
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.internal.repository.eclipse.SigilRepositoryManager;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+
+public class GlobalRepositoryManager extends SigilRepositoryManager implements
+		IRepositoryManager {
+
+	public GlobalRepositoryManager() {
+		super(null);
+	}
+
+	@Override
+	protected IRepositoryModel[] findRepositories() {
+		List<IRepositoryModel> repos = SigilCore.getRepositoryConfiguration().loadRepositories();
+		return repos.toArray( new IRepositoryModel[repos.size()]);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java
new file mode 100644
index 0000000..186e962
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class OSGiInstallRepository extends AbstractBundleRepository {
+	
+	private Map<String,List<ISigilBundle>> bundles;
+	
+	public OSGiInstallRepository(String id) {
+		super(id);
+	}
+	
+	public void refresh() {
+		synchronized( this ) {
+			bundles = null;
+		}
+		
+		notifyChange();		
+	}
+	
+	@Override
+	public void accept(IRepositoryVisitor visitor, int options) {
+		IOSGiInstall install = SigilCore.getInstallManager().getDefaultInstall();
+
+		if ( install != null ) {
+			List<ISigilBundle> found = null;
+
+			synchronized( this ) {
+				found = bundles == null ? null : bundles.get( install.getId() );
+			}
+
+			if ( found == null )  {
+				found = new ArrayList<ISigilBundle>();
+				IPath source = install.getType().getSourceLocation();
+				
+				for ( IPath p : install.getType().getDefaultBundleLocations() ) {
+					loadBundle( p, found, source );
+				}
+
+				synchronized( this ) {
+					bundles = new HashMap<String, List<ISigilBundle>>();
+					bundles.put( install.getId(), found );
+				}
+			}
+
+			for ( ISigilBundle b : found ) {
+				if ( !visitor.visit(b) ) {
+					break;
+				}
+			}
+		}
+	}
+
+	private void loadBundle(IPath p, List<ISigilBundle> bundles, IPath source) {
+		File f = p.toFile();
+		JarFile jar = null;
+		try {
+			jar = new JarFile(f);
+			ISigilBundle bundle = buildBundle(jar.getManifest(), f );
+			if ( bundle != null ) {
+				bundle.setLocation(p);
+				bundle.setSourcePathLocation( source );
+				bundle.setSourceRootPath( new Path( "src" ) );
+				bundles.add( bundle );
+			}
+		} catch (IOException e) {
+			BldCore.error( "Failed to read jar file " + f, e );
+		} catch (ModelElementFactoryException e) {
+			BldCore.error( "Failed to build bundle " + f , e );
+		} catch (RuntimeException e) {
+			BldCore.error( "Failed to build bundle " + f , e );
+		}
+		finally {
+			if ( jar != null ) {
+				try {
+					jar.close();
+				} catch (IOException e) {
+					BldCore.error( "Failed to close jar file", e );
+				}
+			}
+		}
+	}
+
+	private ISigilBundle buildBundle(Manifest manifest, File f) {
+		IBundleModelElement info = buildBundleModelElement( manifest );
+
+		ISigilBundle bundle = null;
+
+		if ( info != null ) {
+			bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+			bundle.addChild(info);
+			bundle.setLocation( new Path( f.getAbsolutePath() ) );
+		}
+
+		return bundle;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java
new file mode 100644
index 0000000..043eb81
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+
+public class OSGiInstallRepositoryProvider implements IRepositoryProvider {
+	public IBundleRepository createRepository(String id, Properties preferences) {
+		return new OSGiInstallRepository(id);
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java
new file mode 100644
index 0000000..0b4b757
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.repository.AbstractRepositoryManager;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+public class SigilRepositoryManager extends AbstractRepositoryManager implements IRepositoryManager, IPropertyChangeListener {
+	
+	private final String repositorySet;
+	
+	private HashMap<String, RepositoryCache> cachedRepositories = new HashMap<String, RepositoryCache>();
+	
+	class RepositoryCache {
+		private final Properties pref;
+		private final IBundleRepository repo;
+
+		RepositoryCache(Properties pref, IBundleRepository repo) {
+			this.pref = pref;
+			this.repo = repo;
+		}
+	}
+	
+	public SigilRepositoryManager(String repositorySet) {
+		this.repositorySet = repositorySet;
+	}
+	
+	@Override
+	public void initialise() {
+		super.initialise();
+		SigilCore.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+	}
+	
+	public void destroy() {
+		IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+		if ( prefs != null ) {
+			prefs.removePropertyChangeListener(this);
+		}
+	}
+		
+	@Override
+	protected void loadRepositories() {
+		IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+		
+		ArrayList<IBundleRepository> repos = new ArrayList<IBundleRepository>();
+		HashSet<String> ids = new HashSet<String>();
+		
+		for ( IRepositoryModel repo : findRepositories() ) {
+			try {
+				IRepositoryProvider provider = findProvider( repo.getType() );
+				String id = repo.getId();
+				IBundleRepository repoImpl = null;
+				if ( repo.getType().isDynamic() ) {
+					String instance = "repository." + repo.getType().getId() + "." + id; 
+					String loc = prefs.getString( instance + ".loc" );
+					Properties pref = loadPreferences(loc);
+					repoImpl = loadRepository(id, pref, provider);
+				}
+				else {
+					repoImpl = loadRepository(id, null, provider);
+				}
+				
+				repos.add( repoImpl );
+				ids.add( id );
+			} catch (Exception e) {
+				SigilCore.error( "Failed to load repository for " + repo, e);
+			}
+		}
+
+		setRepositories(repos.toArray( new IBundleRepository[repos.size()] ) );
+		
+		for ( Iterator<String> i = cachedRepositories.keySet().iterator(); i.hasNext(); ) {
+			if ( !ids.contains(i.next()) ) {
+				i.remove();
+			}
+		}
+	}
+
+	private IRepositoryProvider findProvider(IRepositoryType repositoryType) throws CoreException {
+		String id = repositoryType.getId();
+		
+		IExtensionRegistry registry = Platform.getExtensionRegistry();
+		IExtensionPoint p = registry.getExtensionPoint(SigilCore.REPOSITORY_PROVIDER_EXTENSION_POINT_ID);
+		
+		for ( IExtension e : p.getExtensions() ) {
+			for ( IConfigurationElement c : e.getConfigurationElements() ) {
+				if ( id.equals( c.getAttribute("id") ) ) {
+					IRepositoryProvider provider = (IRepositoryProvider) c.createExecutableExtension("class");
+					return provider;
+				}
+			}
+		}
+
+		return null;
+	}
+
+	protected IRepositoryModel[] findRepositories() {
+		if ( repositorySet == null ) {
+			return SigilCore.getRepositoryConfiguration().getDefaultRepositorySet().getRepositories();
+		}
+		else {
+			IRepositorySet set = SigilCore.getRepositoryConfiguration().getRepositorySet(repositorySet);
+			return set.getRepositories();
+		}	
+	}
+
+	private IBundleRepository loadRepository(String id, Properties pref, IRepositoryProvider provider) throws RepositoryException {
+		try {
+			if ( pref == null ) {
+				pref = new Properties();
+			}
+
+			RepositoryCache cache = cachedRepositories.get(id);
+			
+			if ( cache == null || !cache.pref.equals(pref) ) {
+				IBundleRepository repo = provider.createRepository(id, pref);
+				cache = new RepositoryCache(pref, repo);
+				cachedRepositories.put( id, cache );
+			}
+			
+			return cache.repo;
+		} catch (RuntimeException e) {
+			throw new RepositoryException( "Failed to build repositories", e);
+		}		
+	}
+
+	private Properties loadPreferences(String loc) throws FileNotFoundException, IOException {
+		FileInputStream in = null;
+		try {
+			Properties pref = new Properties();
+			pref.load(new FileInputStream(loc));
+			return pref;
+		}
+		finally {
+			if ( in != null ) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					SigilCore.error( "Failed to close file", e );
+				}
+			}
+		}
+	}
+	
+	public void propertyChange(PropertyChangeEvent event) {
+		if ( event.getProperty().equals( "repository.timestamp" ) ) {
+			loadRepositories();
+		}
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java
new file mode 100644
index 0000000..d144e6f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.CoreException;
+
+public class WorkspaceRepository extends AbstractBundleRepository implements IResourceChangeListener {
+	
+	private static final int UPDATE_MASK = IResourceDelta.CONTENT | IResourceDelta.DESCRIPTION | IResourceDelta.OPEN;
+	private ISigilBundle[] bundles;
+	
+	public WorkspaceRepository(String id) {
+		super(id);
+	}
+
+	@Override
+	public void accept(IRepositoryVisitor visitor, int options) {
+		synchronized( this ) {
+			if ( bundles == null ) {
+				List<ISigilProjectModel> models = SigilCore.getRoot().getProjects();
+				ArrayList<ISigilBundle> tmp = new ArrayList<ISigilBundle>(models.size());
+				for ( ISigilProjectModel n : models ) {
+					ISigilBundle b = n.getBundle();
+					tmp.add(b);
+				}
+				bundles = tmp.toArray( new ISigilBundle[tmp.size()] );
+			}
+		}
+		
+		for ( ISigilBundle b : bundles ) {
+			visitor.visit(b);
+		}
+	}
+	
+	public void refresh() {
+		synchronized(this) {
+			bundles = null;
+		}
+	}
+	
+	@Override
+	protected void notifyChange() {
+		refresh();
+		super.notifyChange();
+	}
+
+	public void resourceChanged(IResourceChangeEvent event) {
+		try {
+			event.getDelta().accept( new IResourceDeltaVisitor() {
+				public boolean visit(IResourceDelta delta) throws CoreException {
+					boolean result;
+					
+					IResource resource = delta.getResource();
+					if(resource instanceof IWorkspaceRoot) {
+						result = true;
+					} else if(resource instanceof IProject) {
+						IProject project = (IProject) resource;
+						if ( SigilCore.isSigilProject(project) ) {
+							switch (delta.getKind()) {
+							case IResourceDelta.CHANGED: 
+								if ( (delta.getFlags() & UPDATE_MASK) == 0 ) {
+									break;
+								}
+								// else 
+								// fall through on purpose
+							case IResourceDelta.ADDED: // fall through on purpose
+							case IResourceDelta.REMOVED: // fall through on purpose
+								notifyChange();
+								break;
+							}
+							result = true;
+						} else {
+							result = false;
+						}
+					} else if(resource.getName().equals(SigilCore.SIGIL_PROJECT_FILE)) {
+						switch(delta.getKind()) {
+						case IResourceDelta.CHANGED:
+						case IResourceDelta.ADDED:
+						case IResourceDelta.REMOVED:
+							notifyChange();
+						}
+						result = false;
+					} else {
+						result = false;
+					}
+ 					return result;
+				}
+			});
+		} catch (CoreException e) {
+			SigilCore.error( "Workspace repository update failed", e ); 
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java
new file mode 100644
index 0000000..b67aa25
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+public class WorkspaceRepositoryProvider implements IRepositoryProvider {
+	private static WorkspaceRepository repository;
+	
+	public static WorkspaceRepository getWorkspaceRepository() {
+		return repository;	
+	}
+	
+	public IBundleRepository createRepository(String id, Properties preferences) {
+		if ( repository == null ) {
+			repository = new WorkspaceRepository(id);
+			ResourcesPlugin.getWorkspace().addResourceChangeListener(repository, IResourceChangeEvent.POST_CHANGE);
+		}
+		return repository;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java
new file mode 100644
index 0000000..243dccf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.job;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+public class ResolveProjectsJob extends Job {
+	
+	private final IWorkspace workspace;
+	private final IProject project;
+	
+	public ResolveProjectsJob(IWorkspace workspace) {
+		super("Resolving Sigil projects");
+		this.workspace = workspace;
+		this.project = null;
+		setRule(ResourcesPlugin.getWorkspace().getRoot());
+	}
+	
+	public ResolveProjectsJob(IProject project) {
+		super("Resolving Sigil project");
+		this.workspace = null;
+		this.project = project;
+		setRule(project.getFile(SigilCore.SIGIL_PROJECT_FILE));
+	}
+	
+	@Override
+	protected IStatus run(IProgressMonitor monitor) {
+		MultiStatus status = new MultiStatus(SigilCore.PLUGIN_ID, 0, "Error resolving Sigil projects", null);
+		
+		Collection<ISigilProjectModel> sigilProjects = null;
+		
+		if(workspace != null) {
+			sigilProjects = SigilCore.getRoot().getProjects();
+		} else if(project != null) {
+			try {
+				ISigilProjectModel sigilProject = SigilCore.create(project);
+				sigilProjects = Collections.singleton(sigilProject);
+			} catch (CoreException e) {
+				status.add(e.getStatus());
+			}
+		}
+
+		if ( sigilProjects != null ) {
+			for (ISigilProjectModel sigilProject : sigilProjects) {
+				try {
+					// Delete existing dependency markers on project
+					sigilProject.getProject().deleteMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE);
+					
+					IRepositoryManager repository = SigilCore.getRepositoryManager(sigilProject);
+					ResolutionMonitorAdapter resolutionMonitor = new ResolutionMonitorAdapter(monitor);
+		
+					IBundleResolver resolver = repository.getBundleResolver();
+					ResolutionConfig config = new ResolutionConfig(ResolutionConfig.IGNORE_ERRORS);
+					
+					// Execute resolver
+					IResolution resolution = resolver.resolve(sigilProject, config, resolutionMonitor);
+					
+					// Find missing imports
+					Set<IPackageImport> imports = sigilProject.getBundle().getBundleInfo().getImports();
+					for (IPackageImport pkgImport : imports) {
+						if(resolution.getProvider(pkgImport) == null) {
+							markMissingImport(pkgImport, sigilProject.getProject());
+						}
+					}
+					
+					// Find missing required bundles
+					Set<IRequiredBundle> requiredBundles = sigilProject.getBundle().getBundleInfo().getRequiredBundles();
+					for (IRequiredBundle requiredBundle : requiredBundles) {
+						if(resolution.getProvider(requiredBundle) == null) {
+							markMissingRequiredBundle(requiredBundle, sigilProject.getProject());
+						}
+					}
+				} catch (ResolutionException e) {
+					status.add(new Status(IStatus.ERROR, SigilCore.PLUGIN_ID, 0, "Error resolving project " + sigilProject.getProject().getName(), e));
+				} catch (CoreException e) {
+					status.add(e.getStatus());
+				}
+			}
+		}
+		
+		return status;
+	}
+	
+	private static void markMissingImport(IPackageImport pkgImport, IProject project) throws CoreException {
+		IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE);
+		marker.setAttribute("element", pkgImport.getPackageName());
+		marker.setAttribute("versionRange", pkgImport.getVersions().toString());
+		marker.setAttribute(IMarker.MESSAGE, "Cannot resolve imported package \"" + pkgImport.getPackageName() + "\" with version range " + pkgImport.getVersions());
+		marker.setAttribute(IMarker.SEVERITY, pkgImport.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR);
+		marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+	}
+	
+	private static void markMissingRequiredBundle(IRequiredBundle req, IProject project) throws CoreException {
+		IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE);
+		marker.setAttribute("element", req.getSymbolicName());
+		marker.setAttribute("versionRange", req.getVersions().toString());
+		marker.setAttribute(IMarker.MESSAGE, "Cannot resolve required bundle \"" + req.getSymbolicName() + "\" with version range " + req.getVersions());
+		marker.setAttribute(IMarker.SEVERITY, req.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR);
+		marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+	}
+	
+	
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java
new file mode 100644
index 0000000..5c526d0
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.job;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class ThreadProgressMonitor {
+	private static ThreadLocal<IProgressMonitor> local = new ThreadLocal<IProgressMonitor>();
+	
+	public static void setProgressMonitor(IProgressMonitor monitor) {
+		local.set(monitor);
+	}
+	
+	public static IProgressMonitor getProgressMonitor() {
+		return local.get();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java
new file mode 100644
index 0000000..2c9b53a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.project;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface ISigilModelRoot {
+	List<ISigilProjectModel> getProjects();
+	
+	Collection<ISigilProjectModel> resolveDependentProjects(ISigilProjectModel sigil, IProgressMonitor monitor);
+
+	Collection<ISigilBundle> resolveBundles(ISigilProjectModel sigil, IModelElement element, boolean includeOptional, IProgressMonitor monitor) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java
new file mode 100644
index 0000000..ebb8fe9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.project;
+
+import java.util.Collection;
+
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.osgi.framework.Version;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Represents a sigil project. To get a reference to a ISigilProjectModel you can use the 
+ * helper method {@link BldCore#create(IProject)}.
+ * 
+ * @author dave
+ *
+ */
+public interface ISigilProjectModel extends ICompoundModelElement {
+    
+    /**
+     * @return
+     */
+    IProject getProject();
+	
+	// shortcut to getProject().getName()
+	String getName();
+	
+	Preferences getPreferences();
+    
+    /**
+	 * 
+	 * @param monitor
+	 *            The progress monitor to use for reporting progress to the
+	 *            user. It is the caller's responsibility to call done() on the
+	 *            given monitor. Accepts null, indicating that no progress
+	 *            should be reported and that the operation cannot be cancelled
+	 * @throws CoreException
+	 */
+	void save(IProgressMonitor monitor) throws CoreException;
+    
+	/**
+	 * @return
+	 */
+	Version getVersion();
+	    
+	String getSymbolicName();
+	
+	ISigilBundle getBundle();
+	
+	void setBundle(ISigilBundle bundle);
+	
+	/**
+	 * @return
+	 */
+	IJavaProject getJavaModel();
+	
+	Collection<ISigilProjectModel> findDependentProjects(IProgressMonitor monitor);
+
+	void resetClasspath(IProgressMonitor monitor) throws CoreException;
+	
+	IPath findBundleLocation() throws CoreException;
+
+	IModelElement findImport(String packageName, IProgressMonitor monitor);
+
+	boolean isInClasspath(String packageName, IProgressMonitor monitor) throws CoreException;
+
+	boolean isInClasspath(ISigilBundle bundle);
+	
+	boolean isInBundleClasspath(IPackageFragmentRoot root) throws JavaModelException;
+	
+	IPath findOutputLocation() throws CoreException;
+
+	IBldProject getBldProject() throws CoreException;
+
+	Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java
new file mode 100644
index 0000000..7c62e6c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.internal.model.repository.RepositoryType;
+import org.eclipse.core.runtime.CoreException;
+
+public interface IRepositoryConfiguration {
+
+	List<IRepositoryModel> loadRepositories();
+	
+	IRepositoryModel findRepository(String id);
+
+	void saveRepositories(List<IRepositoryModel> repositories) throws CoreException;
+
+	List<RepositoryType> loadRepositoryTypes();
+
+	IRepositoryModel newRepositoryElement(IRepositoryType type);
+	
+	IRepositorySet getDefaultRepositorySet();
+
+	void setDefaultRepositorySet(IRepositorySet defaultSet);
+	
+	IRepositorySet getRepositorySet(String name);
+	
+	Map<String, IRepositorySet> loadRepositorySets();
+	
+	void saveRepositorySets(Map<String, IRepositorySet> sets);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java
new file mode 100644
index 0000000..182998f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import org.eclipse.jface.preference.PreferenceStore;
+
+public interface IRepositoryModel {
+
+	String getId();
+
+	void setName(String stringValue);
+	
+	String getName();
+
+	PreferenceStore getPreferences();
+
+	IRepositoryType getType();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java
new file mode 100644
index 0000000..b4e644b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+public interface IRepositorySet {
+	void setRepository(IRepositoryModel id, int position);
+	void removeRepository(IRepositoryModel id);
+	IRepositoryModel[] getRepositories();
+	void setRepositories(IRepositoryModel[] repositories);
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java
new file mode 100644
index 0000000..33df100
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import org.eclipse.swt.graphics.Image;
+
+public interface IRepositoryType {
+
+	String getType();
+
+	String getId();
+
+	Image getIcon();
+
+	boolean isDynamic();
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java
new file mode 100644
index 0000000..5630a6d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class RepositorySet implements IRepositorySet {
+
+	private static final IRepositoryModel[] EMPTY = new IRepositoryModel[0];
+	
+	private IRepositoryModel[] reps;
+
+	public RepositorySet() {
+		this( EMPTY );
+	}
+	
+	public RepositorySet(Collection<IRepositoryModel> reps) {
+		this( reps.toArray( new IRepositoryModel[reps.size()] ) );
+	}
+
+	public RepositorySet(IRepositoryModel[] repositories) {
+		this.reps = repositories;
+	}
+	
+	public void setRepository(IRepositoryModel id, int position) {
+		ArrayList<IRepositoryModel> tmp = new ArrayList<IRepositoryModel>(reps.length + 1);
+		tmp.remove(id);
+		tmp.add(position, id);
+		reps = tmp.toArray( new IRepositoryModel[tmp.size()] );
+	}
+
+	public IRepositoryModel[] getRepositories() {
+		return reps;
+	}
+
+	public void removeRepository(IRepositoryModel id) {
+		ArrayList<IRepositoryModel> tmp = new ArrayList<IRepositoryModel>(reps.length + 1);
+		tmp.remove(id);
+		reps = tmp.toArray( new IRepositoryModel[tmp.size()] );
+	}
+
+	public void setRepositories(IRepositoryModel[] repositories) {
+		reps = repositories == null ? EMPTY : repositories;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java
new file mode 100644
index 0000000..b2c7854
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+public class Grep {
+
+	// Pattern used to parse lines
+	private static Pattern linePattern = Pattern.compile(".*\r?\n");
+	
+	// The input pattern that we're looking for
+	private Pattern pattern;
+	
+	private CharBuffer cb;
+
+	private FileChannel fc;
+	
+	private Grep(IFile f, Pattern pattern) throws IOException, CoreException {
+		this.pattern = pattern;
+		cb = openBuffer(f);			
+	}
+	
+	private CharBuffer openBuffer(IFile f) throws IOException, CoreException {
+		Charset charset = Charset.forName(f.getCharset());
+		CharsetDecoder decoder = charset.newDecoder();
+		// Open the file and then get a channel from the stream
+		FileInputStream fis = new FileInputStream(f.getLocation().toFile());
+		fc = fis.getChannel();
+
+		// Get the file's size and then map it into memory
+		int sz = (int) fc.size();
+		MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
+
+		// Decode the file into a char buffer
+		return decoder.decode(bb);
+	}
+
+	public static String[] grep(Pattern pattern, IFile...files) throws CoreException {
+		LinkedList<String> matches = new LinkedList<String>();
+		for ( IFile f : files ) {
+			try {
+				Grep g = new Grep( f, pattern );
+				g.grep(matches);
+				g.close();
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		return matches.toArray( new String[matches.size()]);
+	}
+
+	private void close() throws IOException {
+		fc.close();
+	}
+
+	// Use the linePattern to break the given CharBuffer into lines, applying
+	// the input pattern to each line to see if we have a match
+	//
+	private void grep(List<String> matches) {
+		Matcher lm = linePattern.matcher(cb); // Line matcher
+		Matcher pm = null; // Pattern matcher
+		int lines = 0;
+		while (lm.find()) {
+			lines++;
+			CharSequence cs = lm.group(); // The current line
+			if (pm == null)
+				pm = pattern.matcher(cs);
+			else
+				pm.reset(cs);
+			if (pm.find())
+				matches.add(pm.group());
+			if (lm.end() == cb.limit())
+				break;
+		}		
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java
new file mode 100644
index 0000000..1c87c87
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java
@@ -0,0 +1,985 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IAccessRule;
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IClassFile;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IImportDeclaration;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.ILocalVariable;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IParent;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class JavaHelper {
+	
+	public static final IAccessRule DENY_RULE = JavaCore.newAccessRule(
+			new Path("**"), IAccessRule.K_NON_ACCESSIBLE
+					| IAccessRule.IGNORE_IF_BETTER);
+
+	public static final IAccessRule ALLOW_ALL_RULE = JavaCore.newAccessRule(
+			new Path("**"), IAccessRule.K_ACCESSIBLE);
+	
+	private static Map<String, Collection<IClasspathEntry>> entryCache = new HashMap<String, Collection<IClasspathEntry>>();
+	
+	public static Collection<IClasspathEntry> findClasspathEntries(ISigilBundle bundle) {
+		LinkedList<IClasspathEntry> cp = new LinkedList<IClasspathEntry>();
+		
+		ISigilProjectModel sp = bundle.getAncestor(ISigilProjectModel.class);
+		
+		if ( sp != null ) {
+			IJavaProject p = sp.getJavaModel();
+			
+			for ( String enc : bundle.getClasspathEntrys() ) {
+				IClasspathEntry e = p.decodeClasspathEntry(enc);
+				if ( e != null ) {
+					cp.add( e );
+				}
+			}
+		}
+		
+		return cp;
+	}
+	
+	public static Collection<ICompilationUnit> findCompilationUnits(ISigilProjectModel project) throws JavaModelException {
+		LinkedList<ICompilationUnit> ret = new LinkedList<ICompilationUnit>();
+		
+		IJavaProject java = project.getJavaModel();
+		for ( IClasspathEntry cp : findClasspathEntries(project.getBundle()) ) {
+			IPackageFragmentRoot[] roots = java.findPackageFragmentRoots(cp);
+			for (IPackageFragmentRoot rt : roots ) {
+				for ( IJavaElement j : rt.getChildren() ) {
+					IPackageFragment p = (IPackageFragment) j;
+					ICompilationUnit[] units = p.getCompilationUnits();
+					for ( ICompilationUnit u : units ) {
+						ret.add( u );
+					}
+				}
+			}
+		}
+		
+		return ret;
+	}
+	
+	/**
+	 * @param project 
+	 * @param packageName
+	 * @return
+	 */
+	public static Collection<IPackageExport> findExportsForPackage(ISigilProjectModel project, final String packageName) {
+		final LinkedList<IPackageExport> results = new LinkedList<IPackageExport>();
+		
+		SigilCore.getRepositoryManager(project).visit(new IModelWalker() {
+			public boolean visit(IModelElement element) {
+				if ( element instanceof IPackageExport ) {
+					IPackageExport e = (IPackageExport) element;
+					if ( e.getPackageName().equals( packageName ) ) {
+						results.add( e );
+					}
+				}				
+				return true;
+			}
+		});
+
+		return results;
+	}
+	
+	public static String[] findUses(String packageName, ISigilProjectModel projectModel) throws CoreException {
+		ArrayList<String> uses = new ArrayList<String>();
+		
+		for ( final String dependency : findPackageDependencies( packageName, projectModel ) ) {
+			if ( !dependency.equals( packageName ) ) {
+				final boolean[] found = new boolean[1];
+				
+				projectModel.visit( new IModelWalker() {
+	
+					public boolean visit(IModelElement element) {
+						if ( element instanceof IPackageImport ) {
+							IPackageImport pi = (IPackageImport) element;
+							if ( pi.getPackageName().equals( dependency ) ) {
+								found[0] = true;
+							}
+						}
+						return !found[0];
+					}				
+				} );
+				
+				if ( found[0] ) {
+					uses.add( dependency );
+				}
+			}
+		}
+		
+		return uses.toArray( new String[uses.size()] );
+	}
+	
+	private static String[] findPackageDependencies(String packageName, ISigilProjectModel projectModel) throws CoreException {
+		HashSet<String> imports = new HashSet<String>();
+
+		IPackageFragment p = (IPackageFragment) projectModel.getJavaModel().findElement( new Path(packageName.replace('.', '/') ) );
+
+		if ( p == null ) {
+			throw SigilCore.newCoreException("Unknown package " + packageName, null);
+		}
+		for (ICompilationUnit cu : p.getCompilationUnits() ) {
+			scanImports(cu, imports);
+		}
+		for (IClassFile cf : p.getClassFiles() ) {
+			scanImports(cf, imports);
+		}
+		
+		return imports.toArray( new String[imports.size()] );		
+	}
+
+	/**
+	 * @param project
+	 * @param monitor
+	 * @return
+	 */
+	public static List<IPackageImport> findRequiredImports(ISigilProjectModel project, IProgressMonitor monitor) {
+		LinkedList<IPackageImport> imports = new LinkedList<IPackageImport>();
+		
+		for ( String packageName : findJavaImports(project, monitor)) {
+			if ( !ProfileManager.isBootDelegate(project, packageName) ) { // these must come from boot classloader
+				try {
+					if ( !project.isInClasspath(packageName, monitor) ) {
+						Collection<IPackageExport> exports = findExportsForPackage(project, packageName);
+						if ( !exports.isEmpty() ) {
+							imports.add( select( exports ) );
+						}
+					}
+				} catch (CoreException e) {
+					SigilCore.error( "Failed to check classpath", e );
+				}
+			}
+		}
+		
+		return imports;
+	}
+	
+	/**
+	 * @param project
+	 * @param monitor
+	 * @return
+	 */
+	public static Collection<IModelElement> findUnusedReferences(final ISigilProjectModel project, final IProgressMonitor monitor) {
+		final LinkedList<IModelElement> unused = new LinkedList<IModelElement>();
+		
+		final Set<String> packages = findJavaImports(project, monitor);
+		
+		project.visit( new IModelWalker() {
+			public boolean visit(IModelElement element) {
+				if ( element instanceof IPackageImport ) {
+					IPackageImport pi = (IPackageImport) element;
+					if ( !packages.contains( pi.getPackageName() ) ) {
+						unused.add( pi );
+					}
+				}
+				else if ( element instanceof IRequiredBundle ) {
+					IRequiredBundle rb = (IRequiredBundle) element;
+					IRepositoryManager manager = SigilCore.getRepositoryManager(project);
+					ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL | ResolutionConfig.IGNORE_ERRORS);
+					try {
+						IResolution r = manager.getBundleResolver().resolve(rb, config ,
+								new ResolutionMonitorAdapter(monitor) );
+						ISigilBundle bundle = r.getProvider(rb);
+						boolean found = false;
+						for ( IPackageExport pe : bundle.getBundleInfo().getExports() ) {
+							if ( packages.contains(pe.getPackageName() ) ) {
+								found = true;
+								break;
+							}
+						}
+						
+						if ( !found ) {
+							unused.add( rb );
+						}
+					} catch (ResolutionException e) {
+						SigilCore.error( "Failed to resolve " + rb, e );
+					}
+				}
+				return true;
+			}
+		});
+		
+		return unused;
+	}
+	
+	public static Collection<IClasspathEntry> resolveClasspathEntrys(ISigilProjectModel sigil, IProgressMonitor monitor)
+	throws CoreException {
+		if ( monitor == null ) {
+			monitor = Job.getJobManager().createProgressGroup();
+			monitor.beginTask("Resolving classpath for "
+					+ sigil.getSymbolicName(), 2);
+		}
+
+		ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
+
+		ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL | ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INDEXED_ONLY | ResolutionConfig.LOCAL_ONLY);
+
+		IResolution resolution;
+		try {
+			resolution = SigilCore.getRepositoryManager(sigil).getBundleResolver().resolve(sigil, config, new ResolutionMonitorAdapter(monitor));
+		} catch (ResolutionException e) {
+			throw SigilCore.newCoreException("Failed to resolve dependencies", e);
+		}
+
+		monitor.worked(1);
+
+		for (ISigilBundle bundle : resolution.getBundles()) {
+			if (!bundle.getBundleInfo().getSymbolicName().equals(sigil.getSymbolicName())) { // discard self reference...
+				List<IModelElement> matched = resolution.getMatchedRequirements(bundle);
+				for (IClasspathEntry cpe : buildClassPathEntry(sigil, bundle, matched, monitor)) {
+					entries.add(cpe);
+				}
+			}
+		}
+
+		monitor.worked(1);
+		monitor.done();
+
+		return entries;
+	}
+
+	private static Collection<IClasspathEntry> buildClassPathEntry(ISigilProjectModel project, ISigilBundle provider, List<IModelElement> requirements, IProgressMonitor monitor) throws CoreException {
+		IAccessRule[] rules = buildAccessRules(project, provider, requirements);
+
+		ISigilProjectModel other = provider.getAncestor(ISigilProjectModel.class);
+
+		try {
+			if (other == null) {
+				provider.synchronize(monitor);
+				return newBundleEntry(provider, rules, null, false);
+			} else {
+				return newProjectEntry(other, rules, null, false);
+			}
+		} catch (IOException e) {
+			throw SigilCore.newCoreException("Failed to synchronize " + provider, e);
+		}
+	}
+
+	private static IAccessRule[] buildExportRules(ISigilBundle bundle) {
+		Set<IPackageExport> ex = bundle.getBundleInfo().getExports();
+		IAccessRule[] rules = new IAccessRule[ex.size() + 1];
+
+		Iterator<IPackageExport> iter = ex.iterator();
+		for (int i = 0; i < rules.length - 1; i++) {
+			IPackageExport p = iter.next();
+			rules[i] = JavaCore.newAccessRule(new Path(p.getPackageName()
+					.replace('.', '/')).append("*"), IAccessRule.K_ACCESSIBLE);
+		}
+
+		rules[rules.length - 1] = DENY_RULE;
+
+		return rules;
+	}
+
+	private static Collection<IClasspathEntry> newProjectEntry(
+			ISigilProjectModel n, IAccessRule[] rules,
+			IClasspathAttribute[] attributes, boolean export)
+			throws CoreException {
+		if (rules == null) {
+			rules = JavaHelper.buildExportRules(n.getBundle());
+		}
+
+		if (attributes == null) {
+			attributes = new IClasspathAttribute[] {};
+		}
+
+		ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
+		entries.add(JavaCore.newProjectEntry(n.getProject().getFullPath(),
+				rules, false, attributes, export));
+		for (IClasspathEntry e : n.getJavaModel().getRawClasspath()) {
+			switch (e.getEntryKind()) {
+			case IClasspathEntry.CPE_LIBRARY:
+				entries.add(JavaCore.newLibraryEntry(e.getPath(), e
+						.getSourceAttachmentPath(), e
+						.getSourceAttachmentRootPath(), rules, attributes,
+						export));
+				break;
+			case IClasspathEntry.CPE_VARIABLE:
+				IPath path = JavaCore.getResolvedVariablePath(e.getPath());
+				if (path != null) {
+					entries.add(JavaCore.newLibraryEntry(path, e
+							.getSourceAttachmentPath(), e
+							.getSourceAttachmentRootPath(), rules, attributes,
+							export));
+				}
+				break;
+			}
+		}
+
+		return entries;
+	}
+
+	private static Collection<IClasspathEntry> newBundleEntry(
+			ISigilBundle bundle, IAccessRule[] rules,
+			IClasspathAttribute[] attributes, boolean exported)
+			throws CoreException {
+		String name = bundle.getBundleInfo().getSymbolicName();
+
+		if (rules == null) {
+			rules = JavaHelper.buildExportRules(bundle);
+		}
+
+		if (attributes == null) {
+			attributes = new IClasspathAttribute[] {};
+		}
+
+		if (bundle.getBundleInfo().getVersion() != null) {
+			name += "_version_" + bundle.getBundleInfo().getVersion();
+		}
+
+		String cacheName = name + rules.toString();
+
+		Collection<IClasspathEntry> entries = null;
+		
+		synchronized( entryCache ) {
+			entries = entryCache.get(cacheName);
+	
+			if (entries == null) {
+				IPath path = bundle.getLocation();
+	
+				if (path == null) {
+					SigilCore.error("Found null path for "
+							+ bundle.getBundleInfo().getSymbolicName());
+					entries = Collections.emptyList();
+				} else {
+					entries = buildEntries(path, name, bundle, rules, attributes,
+							exported);
+				}
+	
+				entryCache.put(cacheName, entries);
+			}
+		}
+		
+		return entries;
+	}
+
+	private static IPath bundleCache = SigilCore.getDefault().getStateLocation().append(
+			"bundle-cache");
+	
+	public static boolean isCachedBundle(String bp) {
+		return bp.startsWith(bundleCache.toOSString());
+	}
+
+	private static Collection<IClasspathEntry> buildEntries(IPath path,
+			String name, ISigilBundle bundle, IAccessRule[] rules,
+			IClasspathAttribute[] attributes, boolean exported)
+			throws CoreException {
+		if (path.toFile().isDirectory()) {
+			throw SigilCore.newCoreException("Bundle location cannot be a directory",
+					null);
+		} else {
+			// ok it's a jar could contain libs etc
+			try {
+				IPath cache = bundleCache.append(name);
+				Set<String> files = unpack(cache, bundle);
+				Set<String> classpath = filterClasspath(bundle.getBundleInfo().getClasspaths(), files);
+				ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(
+						classpath.size());
+
+				for (String cp : classpath) {
+					IPath p = ".".equals(cp) ? path : cache.append(cp);
+					IPath source = bundle.getSourcePathLocation();
+
+					if (source != null && !source.toFile().exists()) {
+						source = null;
+					}
+
+					IClasspathEntry e = JavaCore.newLibraryEntry(p, source,
+							bundle.getSourceRootPath(), rules, attributes,
+							exported);
+					entries.add(e);
+				}
+
+				return entries;
+			} catch (IOException e) {
+				throw SigilCore.newCoreException("Failed to unpack bundle", e);
+			}
+		}
+	}
+
+	private static Set<String> filterClasspath(Collection<String> classpaths,
+			Collection<String> files) {
+		HashSet<String> cp = new HashSet<String>(classpaths);
+		for (String c : cp) {
+			if (".".equals(c)) {
+				// ignore
+			} else {
+				if (!files.remove(c)) {
+					break;
+				}
+			}
+		}
+
+		if (files.isEmpty()) {
+			cp.remove(".");
+		}
+
+		return cp;
+	}
+
+	private static HashMap<IPath, Set<String>> unpacked = new HashMap<IPath, Set<String>>();
+	
+	private static synchronized Set<String> unpack(IPath cache,
+			ISigilBundle bundle) throws IOException {
+		Set<String> files = unpacked.get(cache);
+
+		if ( files == null ) {
+			files = new HashSet<String>();
+			File dir = createEmptyDir(cache);
+			JarInputStream in = new JarInputStream(new FileInputStream(bundle
+					.getLocation().toFile()));
+			JarEntry entry;
+			while ((entry = in.getNextJarEntry()) != null) {
+				File f = new File(dir, entry.getName());
+				if (entry.isDirectory()) {
+					createDir(f);
+				} else {
+					try {
+						File p = f.getParentFile();
+						createDir(p);
+						streamTo(in, f);
+						files.add(entry.getName());
+					}
+					catch (RuntimeException e) {
+						SigilCore.error("Failed to unpack " + entry, e);
+					}
+				}
+			}
+			
+			unpacked.put(cache, files);
+		}
+		return files;
+	}
+
+	private static void createDir(File p) throws IOException {
+		if (!p.exists()) {
+			if (!p.mkdirs())
+				throw new IOException("Failed to create directory " + p);
+		}
+	}
+
+	private static void streamTo(InputStream in, File f) throws IOException {
+		FileOutputStream fos = new FileOutputStream(f);
+		try {
+			byte[] buf = new byte[1024];
+			for (;;) {
+				int r = in.read(buf);
+
+				if (r == -1)
+					break;
+
+				fos.write(buf, 0, r);
+			}
+
+			fos.flush();
+		} finally {
+			try {
+				fos.close();
+			} catch (IOException e) {
+				SigilCore.error("Failed to close stream", e);
+			}
+		}
+	}
+
+	private static File createEmptyDir(IPath cache) {
+		File dir = cache.toFile();
+		if (dir.exists()) {
+			deleteAll(dir);
+		}
+
+		dir.mkdirs();
+		return dir;
+	}
+
+	private static void deleteAll(File file) {
+		File[] sub = file.listFiles();
+		if (sub != null) {
+			for (File f : sub) {
+				deleteAll(f);
+			}
+		}
+		file.delete();
+	}
+
+	private static IAccessRule[] buildAccessRules(ISigilProjectModel project, ISigilBundle bundle, List<IModelElement> requirements) throws JavaModelException {
+		ArrayList<IAccessRule> rules = new ArrayList<IAccessRule>();
+
+		for (IModelElement e : requirements) {
+			if (e instanceof IRequiredBundle) {
+				IRequiredBundle host = project.getBundle().getBundleInfo().getFragmentHost();
+				if ( host != null ) {
+					if ( host.equals(e) ) {
+						return new IAccessRule[] { ALLOW_ALL_RULE };
+					}
+					else {
+						return buildExportRules(bundle);
+					}
+				}
+				else {
+					return buildExportRules(bundle);
+				}
+			} else if (e instanceof IPackageImport) {
+				IPackageImport pi = (IPackageImport) e;
+				String pckg = pi.getPackageName();
+				HashSet<String> pckgs = new HashSet<String>();
+				pckgs.add(pckg);
+				//findIndirectReferences(pckgs, pckg, project.getJavaModel(), project.getJavaModel());
+				
+				for ( String p : pckgs ) {
+					rules.add(newPackageAccess(p));
+				}
+			}
+		}
+
+		rules.add(DENY_RULE);
+
+		return rules.toArray(new IAccessRule[rules.size()]);
+	}
+
+	/*
+	 * Searches for C (and D, E, etc) in case:
+	 * A extends B extends C where A, B and C are in different packages and A is in this bundle
+	 * and B and C are in one or more external bundles
+	 */
+	private static void findIndirectReferences(Set<String> indirect, String pckg, IParent parent, IJavaProject p) throws JavaModelException {
+		for  ( IJavaElement e : parent.getChildren() ) {
+			boolean skip = false;
+			switch ( e.getElementType() ) {
+			case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+				IPackageFragmentRoot rt = (IPackageFragmentRoot) e;
+				IClasspathEntry ce = rt.getRawClasspathEntry();
+				IPath path = ce.getPath();
+				skip = "org.eclipse.jdt.launching.JRE_CONTAINER".equals(path.toString());
+				break;
+			case IJavaElement.CLASS_FILE:
+				IClassFile cf = (IClassFile) e;
+				if ( cf.getElementName().startsWith(pckg) ) {
+					findIndirectReferences(indirect, findPackage(cf.getType().getSuperclassName()), p, p);
+				}
+				break;
+			case IJavaElement.COMPILATION_UNIT:
+				ICompilationUnit cu = (ICompilationUnit) e;
+				break;
+			}
+			
+			if ( !skip && e instanceof IParent ) {
+				IParent newParent = (IParent) e;
+				findIndirectReferences(indirect, pckg, newParent, p);
+			}
+		}
+	}
+	
+	private static IAccessRule newPackageAccess(String packageName) {
+		return JavaCore.newAccessRule(new Path(packageName.replace('.', '/'))
+				.append("*"), IAccessRule.K_ACCESSIBLE);
+	}
+
+	private static Set<String> findJavaImports(ISigilProjectModel project, IProgressMonitor monitor) {
+		Set<String> imports = new HashSet<String>();
+		
+		findJavaModelImports(project, imports, monitor);
+		findSCAImports(project, imports, monitor);
+		findTextImports(project, imports, monitor);
+		
+		return imports;
+	}
+	
+	private static void findSCAImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+		for ( ISCAComposite sca : project.getBundle().getComposites() ) {
+			IFile f = project.getProject().getFile(sca.getLocation());
+			if ( f.exists() ) {
+				try {
+					// TODO for now just treats sca as text files - should build in richer model that is able to detect java elements
+					parseText( f, imports );
+				} catch (CoreException e) {
+					SigilCore.error( "Failed to parse sca file " + f, e );
+				}
+			}
+		}
+	}
+
+	private static void findTextImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+		IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+		IContentType txt = contentTypeManager.getContentType("org.eclipse.core.runtime.text");
+		for ( IPath p : project.getBundle().getSourcePaths() ) {
+			IFile f = project.getProject().getFile(p);
+			if ( f.exists() ) {
+				try {
+					IContentDescription desc = f.getContentDescription();
+					if ( desc != null ) {
+						IContentType type = desc.getContentType();
+						if ( type != null ) {
+							if ( type.isKindOf( txt ) ) {
+								parseText( f, imports );
+							}
+						}
+					}
+				} catch (CoreException e) {
+					SigilCore.error( "Failed to parse text file " + f, e );
+				}
+			}
+		}
+	}
+
+	private static void findJavaModelImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+		try {
+			for ( IPackageFragment root : project.getJavaModel().getPackageFragments() ) {
+				IPackageFragmentRoot rt = (IPackageFragmentRoot) root.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+				
+				if ( isInClassPath( project, rt ) ) {
+					for ( ICompilationUnit cu : root.getCompilationUnits() ) {
+						scanImports( cu, imports );
+					}
+					
+					for ( IClassFile cf : root.getClassFiles() ) {
+						scanImports( cf, imports );
+					}
+				}
+			}
+		} catch (JavaModelException e) {
+			SigilCore.error( "Failed to parse java model", e );
+		}
+	}
+
+	// matches word.word.word.word.Word
+	private static final Pattern JAVA_CLASS_PATTERN = Pattern.compile("((\\w*\\.\\w*)+?)\\.[A-Z]\\w*");
+	
+	private static void parseText(IFile f, Set<String> imports) throws CoreException {
+		for ( String result : Grep.grep( JAVA_CLASS_PATTERN, f) ) {
+			findImport(result, imports);
+		}
+	}
+
+	private static boolean isInClassPath(ISigilProjectModel project, IPackageFragmentRoot rt) throws JavaModelException {
+		String path = encode( project, rt.getRawClasspathEntry() );
+		return project.getBundle().getClasspathEntrys().contains( path );
+	}
+
+	private static String encode(ISigilProjectModel project, IClasspathEntry cp) {
+		return project.getJavaModel().encodeClasspathEntry(cp).trim();
+	}
+	
+	private static void scanImports(IParent parent, Set<String> imports) throws JavaModelException {
+		for ( IJavaElement e : parent.getChildren() ) {
+			switch ( e.getElementType() ) {
+			case IJavaElement.TYPE:
+				handleType( (IType) e, imports );
+				break;
+			case IJavaElement.IMPORT_DECLARATION:
+				handleImport( (IImportDeclaration) e, imports );
+				break;
+			case IJavaElement.FIELD:
+				handleField( (IField) e, imports );
+				break;
+			case IJavaElement.LOCAL_VARIABLE:
+				handleLocalVariable( (ILocalVariable) e, imports );
+				break;
+			case IJavaElement.ANNOTATION:
+				handleAnnotation( (IAnnotation) e, imports );
+				break;
+			case IJavaElement.METHOD:
+				handleMethod( (IMethod) e, imports );
+				break;
+			default:
+				// no action
+				break;
+			}
+			
+			if ( e instanceof IParent ) {
+				scanImports((IParent) e, imports);
+			}
+		}
+	}
+
+	private static void handleType(IType e, Set<String> imports) throws JavaModelException {
+		findImportFromType(e.getSuperclassTypeSignature(), imports);
+		for ( String sig : e.getSuperInterfaceTypeSignatures() ) {
+			findImportFromType(sig, imports);
+		}
+		//findImportsForSuperTypes(e, imports);
+	}
+	
+	/*private static void findImportsForSuperTypes(IType e, Set<String> imports) throws JavaModelException {
+		IJavaProject project = (IJavaProject) e.getAncestor(IJavaModel.JAVA_PROJECT);
+		LinkedList<String> types = new LinkedList<String>();
+		types.add( decodeSignature(e.getSuperclassTypeSignature()) );
+		for ( String sig : e.getSuperInterfaceTypeSignatures() ) {
+			types.add( decodeSignature(sig) );
+		}
+		
+		for ( IPackageFragmentRoot root : project.getPackageFragmentRoots() ) {
+			// only need to search binary files for inheritance as source will automatically be searched
+			if ( root.getKind() == IPackageFragmentRoot.K_BINARY ) {
+				for ( String t : types ) {
+					String pac = findPackage(t);
+					if ( pac != null ) {
+						IPackageFragment fragment = root.getPackageFragment(pac);
+						if ( fragment != null ) {
+							IClassFile c = fragment.getClassFile(findClass(t));
+							if ( c != null ) {
+								findImportsForSuperTypes(c.getType(), imports);
+							}
+						}
+					}
+				}
+			}
+		}
+	} */
+	
+	private static void handleMethod(IMethod e, Set<String> imports) throws JavaModelException {
+		findImportFromType(e.getReturnType(), imports);
+		
+		for ( String param : e.getParameterTypes() ) {
+			findImportFromType(param, imports);
+		}
+		
+		for ( String ex : e.getExceptionTypes() ) {
+			findImportFromType( ex, imports );
+		}
+	}
+
+	private static void handleAnnotation(IAnnotation e, Set<String> imports) {
+		findImport(e.getElementName(), imports);
+	}
+
+	private static void handleLocalVariable(ILocalVariable e, Set<String> imports) {
+		findImportFromType(e.getTypeSignature(), imports);
+	}
+
+	private static void handleField(IField e, Set<String> imports) throws IllegalArgumentException, JavaModelException {
+		findImportFromType(Signature.getElementType(e.getTypeSignature()), imports);
+	}
+
+	private static void handleImport(IImportDeclaration id, Set<String> imports) {
+		findImport( id.getElementName(), imports);
+	}
+
+	private static void findImportFromType(String type, Set<String> imports) {
+		String element = decodeSignature(type);
+		findImport(element, imports);
+	}
+
+	private static String decodeSignature(String type) {
+		return decodeSignature(type, false);
+	}
+	
+	private static String decodeSignature(String type, boolean resolve) {
+		if ( type == null ) {
+			return null;
+		}
+		
+		if ( type.length() > 0 ) {
+			switch ( type.charAt(0) ) {
+			case Signature.C_ARRAY:
+				return decodeSignature(type.substring(1) );
+			case Signature.C_UNRESOLVED:
+				return resolve ? resolve( type.substring(1, type.length() - 1) ) : null;
+			case Signature.C_RESOLVED:
+				return type.substring(1);
+			}
+		}
+		return type;
+	}
+
+	private static String resolve(String substring) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	
+	private static void findImport(String clazz, Set<String> imports) {
+		String packageName = findPackage(clazz);
+		if ( packageName != null ) {
+			imports.add(packageName);
+		}
+	}
+	
+	private static String findPackage(String clazz) {
+		if ( clazz == null ) return null;
+		int pos = clazz.lastIndexOf( '.' );
+		return pos == -1 ? null : clazz.substring(0, pos);
+	}
+
+	private static String findClass(String clazz) {
+		if ( clazz == null ) return null;
+		int pos = clazz.lastIndexOf( '.' );
+		return pos == -1 ? null : clazz.substring(pos + 1);
+	}
+	
+	private static IPackageImport select(Collection<IPackageExport> proposals) {
+		IPackageExport pe = null;
+		
+		for ( IPackageExport check : proposals ) {
+			if ( pe == null || check.getVersion().compareTo( pe.getVersion() ) > 0 ) {
+				pe = check;
+			}
+		}
+		
+		String packageName = pe.getPackageName();
+		
+		IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+		VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+		VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+		Version version = pe.getVersion();
+		VersionRange versions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+		
+		IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+		pi.setPackageName(packageName);
+		pi.setVersions(versions);
+		
+		return pi;
+	}
+
+	public static Iterable<IJavaElement> findTypes(IParent parent, int... type) throws JavaModelException {
+		LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
+		scanForElement(parent, type, found, false);
+		return found;
+	}
+	
+	public static IJavaElement findType(IParent parent, int... type) throws JavaModelException {
+		LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
+		scanForElement(parent, type, found, true);
+		return found.isEmpty() ? null : found.getFirst();
+	}
+
+	private static void scanForElement(IParent parent, int[] type, List<IJavaElement> roots, boolean thereCanBeOnlyOne) throws JavaModelException {
+		for ( IJavaElement e : parent.getChildren() ) {
+			if ( isType(type, e) ) {
+				roots.add( e );
+				if ( thereCanBeOnlyOne ) {
+					break;
+				}
+			}
+			else if ( e instanceof IParent ) {
+				scanForElement( (IParent) e, type, roots, thereCanBeOnlyOne );
+			}
+		}
+	}
+
+	private static boolean isType(int[] type, IJavaElement e) {
+		for ( int i : type ) {
+			if ( i == e.getElementType() ) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static boolean isAssignableTo(String ifaceOrParentClass, IType type) throws JavaModelException {
+		if ( ifaceOrParentClass == null ) return true;
+		
+		ITypeHierarchy h = type.newSupertypeHierarchy(null);
+		
+		for ( IType superType : h.getAllClasses() ) {
+			String name = superType.getFullyQualifiedName();
+			if ( name.equals( ifaceOrParentClass ) ) {
+				return true;
+			}
+		}
+		for ( IType ifaceType : h.getAllInterfaces() ) {
+			String name = ifaceType.getFullyQualifiedName();
+			if ( name.equals( ifaceOrParentClass ) ) {
+				return true;
+			}
+		}
+		
+		return false;
+	}
+
+	private static IType findType(ITypeRoot root) throws JavaModelException {
+		// TODO Auto-generated method stub
+		for ( IJavaElement child : root.getChildren() ) {
+			if ( child.getElementType() == IJavaElement.TYPE ) {
+				return (IType) child;
+			}
+		}
+		
+		throw new JavaModelException( new IllegalStateException( "Missing type for " + root) , IStatus.ERROR );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java
new file mode 100644
index 0000000..a9f5017
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class ModelHelper {
+	public static List<IModelElement> findUsers(IModelElement e) {
+		LinkedList<IModelElement> users = new LinkedList<IModelElement>();
+		
+		findUsers(e, users);
+		
+		return users;
+	}
+
+	private static void findUsers(IModelElement e, final LinkedList<IModelElement> users) {
+		if ( e instanceof IPackageExport ) {
+			final IPackageExport pe = (IPackageExport) e;
+			SigilCore.getGlobalRepositoryManager().visit( new IModelWalker() {
+				public boolean visit(IModelElement element) {
+					if ( element instanceof IPackageImport ) {
+						IPackageImport pi = (IPackageImport) element;
+						if ( pi.accepts( pe ) ) {
+							users.add( pi );
+						}
+						return false;
+					}
+					
+					return true;
+				} 
+			} );
+		}
+		else if ( e instanceof IBundleModelElement ) {
+			final IBundleModelElement bndl = (IBundleModelElement) e;
+			
+			SigilCore.getGlobalRepositoryManager().visit( new IModelWalker() {
+				public boolean visit(IModelElement element) {
+					if ( element instanceof IRequiredBundle ) {
+						IRequiredBundle req = (IRequiredBundle) element;
+						if ( req.accepts( bndl ) ) {
+							users.add( req );
+						}
+						return false;
+					}
+					return true;
+				} 
+			} );
+		}
+		
+		if ( e instanceof ICompoundModelElement ) {
+			ICompoundModelElement c = (ICompoundModelElement) e;
+			IModelElement[] ch = c.children();
+			for ( IModelElement ee : ch ) {
+				findUsers( ee, users );
+			}
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java
new file mode 100644
index 0000000..b42287c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+
+public class ProfileManager {
+	private static final Pattern[] BOOT_DELEGATION_PATTERNS = new Pattern[] {
+		GlobCompiler.compile("org.ietf.jgss"),
+		GlobCompiler.compile("org.omg.*"),
+		GlobCompiler.compile("org.w3c.*"),
+		GlobCompiler.compile("org.xml.*"),
+		GlobCompiler.compile("sun.*"),
+		GlobCompiler.compile("com.sun.*"),
+	};
+	
+	private static HashMap<String, Properties> profiles;
+
+	public static boolean isBootDelegate(ISigilProjectModel project, String packageName) {
+		if ( packageName.startsWith( "java." ) ) {
+			return true;
+		}
+		
+		for ( Pattern p : BOOT_DELEGATION_PATTERNS ) {
+			if ( p.matcher(packageName).matches()) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	public static Properties findProfileForVersion(String javaVersion) {
+		Map<String, Properties> profiles = loadProfiles();
+		
+		if ( "1.5.0".equals( javaVersion ) ) {
+			return profiles.get( "J2SE-1.5" );
+		}
+		else if ( "1.6.0".equals( javaVersion ) ) {
+			return profiles.get( "J2SE-1.6" );
+		}
+		
+		return null;
+	}
+	
+	private synchronized static Map<String, Properties> loadProfiles() {
+		if ( profiles == null ) {
+			profiles = new HashMap<String, Properties>();
+			
+			Bundle b = Platform.getBundle("org.eclipse.osgi");
+			
+			for ( String profile : loadProfiles( b )) {
+				if ( profile.trim().length() > 0 ) {
+					URL url = findURL(profile, b);
+					if ( url != null ) {
+						try {
+							Properties p = loadProperties(url);
+							String name = p.getProperty("osgi.java.profile.name");
+							if ( name != null ) {
+								profiles.put(name, p);
+							}
+							else {
+								SigilCore.error( "Invalid profile definition, no name specified: " + url);
+							}
+						} catch (IOException e) {
+							SigilCore.error( "Failed to load java profile", e );
+						}
+					}
+					else {
+						SigilCore.error( "Unknown profile **" + profile + "**" );
+					}
+				}
+				// else ignore empty values
+			}
+		}
+		return profiles;
+	}
+
+	private static String[] loadProfiles(Bundle b) {
+		URL url = findURL("profile.list", b);
+
+		if ( url != null ) {
+			try {
+				Properties p = loadProperties(url);
+				String s = p.getProperty("java.profiles");
+				return s == null ? new String[] {} : s.split(",");
+			} catch (IOException e) {
+				SigilCore.error( "Failed to load java profile list", e );
+			}
+		}
+		else {
+			SigilCore.error( "Failed to find java profile list" );
+		}
+		
+		// fine no properties found
+		return new String[] {};
+	}
+
+	@SuppressWarnings("unchecked")
+	private static URL findURL(String file, Bundle b) {
+		Enumeration e = b.findEntries("/", file, false);
+		return e == null ? null : (URL) (e.hasMoreElements() ? e.nextElement() : null);
+	}
+
+
+	private static Properties loadProperties(URL url) throws IOException {
+		Properties p = new Properties();
+		
+		InputStream in = null;
+		
+		try {
+			in = url.openStream();
+			p.load(in);
+		}
+		finally {
+			if ( in != null ) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		}
+		
+		return p;
+	}
+
+
+
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java
new file mode 100644
index 0000000..b425fa2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.nature;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+public class SigilProjectNature implements IProjectNature {
+
+    private IProject project;
+    
+    public void configure() throws CoreException {
+        // TODO configure project builder
+
+    }
+
+    public void deconfigure() throws CoreException {
+    }
+
+    public IProject getProject() {
+        return project;
+    }
+
+    public void setProject(IProject project) {
+        this.project = project;
+    }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java
new file mode 100644
index 0000000..0635220
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.text.StrTokenizer;
+
+public class PrefsUtils {
+
+	private PrefsUtils() {
+	}
+
+	public static final String arrayToString(String[] array) {
+		StringBuilder builder = new StringBuilder();
+
+		for (int i = 0; i < array.length; i++) {
+			if (i > 0)
+				builder.append(',');
+			builder.append(StringEscapeUtils.escapeCsv(array[i]));
+		}
+
+		return builder.toString();
+	}
+
+	public static final String[] stringToArray(String string) {
+		StrTokenizer tokenizer = new StrTokenizer(string, ',', '"');
+		String[] array = new String[tokenizer.size()];
+
+		for (int i = 0; i < array.length; i++) {
+			array[i] = tokenizer.nextToken();
+		}
+
+		return array;
+	}
+
+	public static String listToString(List<String> names) {
+		return arrayToString(names.toArray( new String[names.size()]));
+	}
+
+	public static List<String> stringToList(String string) {
+		return new ArrayList<String>(Arrays.asList(stringToArray(string)));
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java
new file mode 100644
index 0000000..4feb070
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+public enum PromptablePreference {
+	Always, Prompt, Never
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java
new file mode 100644
index 0000000..6d2e1e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.internal.model.repository.RepositoryConfiguration;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class SigilPreferencesInitializer extends AbstractPreferenceInitializer {
+	
+	public static final String[] EXCLUDED_RESOURCES = new String[] {
+		".project", ".classpath", ".settings"
+	};
+	
+	@Override
+	public void initializeDefaultPreferences() {
+		IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+
+		store.setDefault(SigilCore.OSGI_INSTALL_CHECK_PREFERENCE, true);
+
+		store.setDefault(SigilCore.DEFAULT_VERSION_LOWER_BOUND, VersionRangeBoundingRule.Micro.name());
+		store.setDefault(SigilCore.DEFAULT_VERSION_UPPER_BOUND, VersionRangeBoundingRule.Any.name());
+
+		store.setDefault(SigilCore.DEFAULT_EXCLUDED_RESOURCES, PrefsUtils.arrayToString(EXCLUDED_RESOURCES));
+		
+		store.setDefault(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL, false);
+		
+		store.setDefault(SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, PromptablePreference.Prompt.name());
+		
+		store.setDefault(SigilCore.PREFERENCES_REBUILD_PROJECTS, PromptablePreference.Prompt.name() );
+		
+		store.setDefault(RepositoryConfiguration.REPOSITORY_DEFAULT_SET, "org.cauldron.sigil.core.workspaceprovider" );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java
new file mode 100644
index 0000000..ae85568
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.property;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+
+public class SigilPropertyTester extends PropertyTester {
+
+	public SigilPropertyTester() {
+	}
+
+	public boolean test( Object receiver, String property, Object[] args, Object expectedValue ) {
+		IResource resource = (IResource) receiver;
+		if ( "isSigilProject".equals( property ) ) {
+			return expectedValue.equals( isSigilProjectLikeResource( resource ) );
+		}
+		return false;
+	}
+
+	/**
+	 * @param resource
+	 * @return
+	 */
+	private static boolean isSigilProjectLikeResource(IResource resource) {
+		if ( resource instanceof IProject ) {
+			IProject p = (IProject) resource;
+			return SigilCore.isSigilProject(p);
+		}
+		else {
+			return resource.getName().equals( SigilCore.SIGIL_PROJECT_FILE );
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.feature/.project b/sigil/org.cauldron.sigil.feature/.project
new file mode 100644
index 0000000..6d3d7cf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.feature/build.properties b/sigil/org.cauldron.sigil.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/sigil/org.cauldron.sigil.feature/feature.xml b/sigil/org.cauldron.sigil.feature/feature.xml
new file mode 100644
index 0000000..9e3dc15
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/feature.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<feature
+      id="org.cauldron.sigil.feature"
+      label="Sigil SDK"
+      version="0.8.0.qualifier"
+      provider-name="Paremus Ltd">
+
+   <description>
+      Sigil plugin, allow developers to build OSGi applications for deployment in newton within eclipse.
+   </description>
+
+   <copyright>
+      Copyright 2009 Paremus Limited, London, United Kingdom
+   </copyright>
+
+   <license url="http://www.apache.org/licenses/LICENSE-2.0">
+      Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+   </license>
+
+   <url>
+      <update url="http://replace.with.real.url"/>
+   </url>
+
+   <requires>
+      <import plugin="org.eclipse.ant.core"/>
+      <import plugin="org.eclipse.core.expressions"/>
+      <import plugin="org.eclipse.core.runtime"/>
+      <import plugin="org.eclipse.debug.ui"/>
+      <import plugin="org.eclipse.jdt.core"/>
+      <import plugin="org.eclipse.jdt.ui"/>
+      <import plugin="org.eclipse.jface.text"/>
+      <import plugin="org.eclipse.pde.ui"/>
+      <import plugin="org.eclipse.ui.editors"/>
+      <import plugin="org.eclipse.ui.forms"/>
+      <import plugin="org.eclipse.ui.ide"/>
+      <import plugin="org.eclipse.ui.workbench.texteditor"/>
+      <import plugin="org.eclipse.ui"/>
+      <import plugin="org.eclipse.osgi"/>
+      <import plugin="org.eclipse.jdt.launching"/>
+      <import plugin="org.eclipse.ui.console"/>
+      <import plugin="org.eclipse.jdt.debug.ui"/>
+      <import plugin="org.eclipse.ui.editors" version="3.4.0" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.jface.text" version="3.4.0" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.ui.ide" version="3.4.0" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.help"/>
+      <import plugin="org.eclipse.zest.core" version="1.0.0" match="compatible"/>
+      <import plugin="org.eclipse.zest.layouts" version="1.0.0" match="compatible"/>
+      <import plugin="org.eclipse.ltk.core.refactoring"/>
+      <import plugin="org.eclipse.help" version="3.3.1" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.ui.cheatsheets" version="3.3.100" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.core.resources"/>
+      <import plugin="org.eclipse.equinox.common" version="3.4.0" match="greaterOrEqual"/>
+      <import plugin="org.cauldron.sigil.ui" version="0.5.5" match="greaterOrEqual"/>
+      <import plugin="org.cauldron.sigil.core" version="0.5.5" match="greaterOrEqual"/>
+   </requires>
+
+   <plugin
+         id="org.cauldron.sigil.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.sigil.ui"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.sigil.help"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.sigil.utils"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.bld.core"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.sigil.search"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.help/.classpath b/sigil/org.cauldron.sigil.help/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.help/.project b/sigil/org.cauldron.sigil.help/.project
new file mode 100644
index 0000000..3e3bb3b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.help</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1174b4b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Fri Aug 01 14:46:19 BST 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..97cd5ab
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Help Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.help;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.help.Activator
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.help;bundle-version="3.3.1",
+ org.eclipse.ui.cheatsheets;bundle-version="3.3.100",
+ org.cauldron.sigil.ui;bundle-version="0.5.5",
+ org.eclipse.ui.ide;bundle-version="3.4.0",
+ org.cauldron.sigil.core;bundle-version="0.5.5"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+Import-Package: org.cauldron.sigil.model.eclipse,
+ org.eclipse.core.resources
diff --git a/sigil/org.cauldron.sigil.help/build.properties b/sigil/org.cauldron.sigil.help/build.properties
new file mode 100644
index 0000000..5bab7a8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               html/,\
+               toc.xml,\
+               cheatsheets/,\
+               archive/
diff --git a/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml b/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml
new file mode 100644
index 0000000..94ce68d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<cheatsheet title="Create a Hello World composite application">
+	<intro>
+		<description>This cheat sheet shows you how to create a simple hello world composite application using Sigil.</description>
+	</intro>
+	
+	<item title="Create an empty Newton project" href="/org.cauldron.sigil.help/html/tasks/new_project.html" dialog="true" skip="true">
+		<description>
+		First, you must create a new &quot;org.example.helloworld&quot; empty Newton project to house the java classes and SCA composites.
+		</description>
+
+		<subitem label="Select &quot;File &gt;New &gt;Project...&quot;. Expand &quot;Sigil&quot;, select &quot;Newton Project&quot; and click &quot;Next&quot;." skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.OpenEmptyNewtonProjectWizardAction"
+				param1="org.example.helloworld"  
+				translate="" />
+		</subitem>
+
+		<subitem label="Enter &quot;org.example.helloworld&quot; as the &quot;Project name&quot;, then click &quot;Finish&quot;." skip="false" />
+	</item>
+	
+	<item title="Create The Java Interfaces and Classes" skip="true">
+		<description>
+		Create a new &quot;org.example.helloworld.api&quot; package and add a HelloWorld interface to this package. 
+		Then create a new &quot;org.example.helloworld.impl&quot; package and add a HelloWorldImpl class which implements HelloWorld to the impl package.
+		Finally create a new &quot;org.example.helloworld.cli&quot; package and add a HelloWorldCli class which references the HelloWorld api to the cli package.
+		</description>
+
+		<subitem label="Create the HelloWorld API" skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+				param1="org.example.helloworld"  
+				param2="src/org/example/helloworld/api"
+				param3="org.cauldron.sigil.help"
+				param4="archive/java/HelloWorld.java"
+				param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+				translate="" />
+		</subitem>
+		<subitem label="Create the HelloWorld Impl" skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+				param1="org.example.helloworld"  
+				param2="src/org/example/helloworld/impl"
+				param3="org.cauldron.sigil.help"
+				param4="archive/java/HelloWorldImpl.java"   
+				param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+				translate="" />
+		</subitem>
+		<subitem label="Create the HelloWorld CLI" skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+				param1="org.example.helloworld"  
+				param2="src/org/example/helloworld/cli"
+				param3="org.cauldron.sigil.help"
+				param4="archive/java/HelloWorldCli.java"   
+				param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+				translate="" />
+		</subitem>
+	</item>
+   <item
+         title="Add OSGi Package Imports To The HelloWorld Project">
+      <description>
+         The HelloWorldCli class references classes which are exported from other OSGi bundles. In order to satisfy the OSGi classloading rules the HelloWorld project must import the required packages.
+      </description>
+      <subitem
+            label="Open the &quot;org.example.helloworld&quot; project editor by double-clicking on the &quot;sigil.properties&quot; file in the project directory."
+            skip="false">
+      </subitem>
+      <subitem
+            label="Select the &quot;Overview&quot; tab.">
+      </subitem>
+      <subitem
+            label="Click on the &quot;Resolve missing dependencies&quot; link in the &quot;Tools&quot; section.">
+      </subitem>
+      <subitem
+            label="Wait for the background task to complete. When the &quot;Review New Imports&quot; dialog appears, click &quot;OK&quot;.">
+      </subitem>
+   </item>
+  
+	
+	<item title="Create The SCA Composites That Describe The Java Resources" skip="true">
+		<description>
+		Create a new HelloWorld service composite that exports the HelloWorldImpl class using the HelloWorld interface. 
+		Then create a new HelloWorld cli composite that imports the HelloWorld interface and exports a ConsoleCommandHandler 
+		service to allow user interaction via the command handler.
+		</description>
+
+		<subitem label="Create the HelloWorld Service" skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+				param1="org.example.helloworld"
+				param2="sca"
+				param3="org.cauldron.sigil.help"
+				param4="archive/sca/org.example.helloworld.service.composite"
+				param5="org.cauldron.sigil.editors.SCACompositeEditor"   
+				translate="" />
+		</subitem>
+		<subitem label="Create the HelloWorld Command Line" skip="true">
+			<action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+				param1="org.example.helloworld"
+				param2="sca"
+				param3="org.cauldron.sigil.help"
+				param4="archive/sca/org.example.helloworld.cli.composite"   
+				param5="org.cauldron.sigil.editors.SCACompositeEditor"   
+				translate="" />
+		</subitem>
+	</item>
+	
+   <item
+         title="Add The SCA Composites To The HelloWorld Bundle">
+      <description>
+         In order to load a composite in a Newton container it needs to be associated with an OSGi bundle that will act as the classpath for that bundle.
+      </description>
+      <subitem
+            label="Open the &quot;org.example.helloworld&quot; project editor by double-clicking on the &quot;sigil.properties&quot; file in the project directory.">
+      </subitem>
+      <subitem
+            label="Select the &quot;Exports&quot; tab.">
+      </subitem>
+      <subitem
+            label="Click on the &quot;Add&quot; button in the Composites section.">
+      </subitem>
+      <subitem
+            label="Add the &quot;org.example.helloworld.service&quot; and &quot;org.example.helloworld.cli&quot; composites to the OSGi bundle.">
+      </subitem>
+      <subitem
+            label="Save the &quot;org.example.helloworld&quot; project editor.">
+      </subitem>
+   </item>
+	
+ <item
+       title="Launch A Newton Container">
+    <description>
+       Launch a Newton enabled JVM in which to host the SCA composites you have just created.
+    </description>
+    <subitem
+          label="Make sure that the the &quot;org.example.helloworld&quot; project editor is open.">
+    </subitem>
+    <subitem
+          label="Select the &quot;Overview&quot; tab.">
+    </subitem>
+    <subitem
+          label="Click on the &quot;Launch a newton container&quot; link in the &quot;Testing&quot; section.">
+    </subitem>
+    <subitem
+          label="Wait for the newton container to finish launching. This is signified by the console message: &quot;Boot complete&quot;">
+    </subitem>
+ </item>
+
+ <item
+       title="Install The HelloWorld Composites In The Newton Container">
+    <description>
+       We will now install the two composites in the Newton container we just launched using the short-cut menu items in the eclipse navigation bar.
+    </description>
+    <subitem
+          label="Open the &quot;org.example.helloworld.service.composite&quot; document using the Sigil &quot;SCA Composite Editor&quot;.">
+    </subitem>
+    <subitem
+          label="Click on the &quot;Install Composite&quot; short-cut menu item in the navigation bar.">
+    </subitem>
+    <subitem
+          label="Select the &quot;org.example.helloworld&quot; instance and click ok">
+    </subitem>
+    <subitem
+          label="Open the &quot;org.example.helloworld.cli.composite&quot; document using the Sigil &quot;SCA Composite Editor&quot;.">
+    </subitem>
+    <subitem
+          label="Again, click on the &quot;Install Composite&quot; short-cut menu item in the navigation bar and install into the &quot;org.example.helloworld&quot; Newton instance.">
+    </subitem>
+ </item>
+	
+	<item title="Type &quot;sayhello&quot; on the Newton console" skip="false">
+		<description>
+		
+		</description>
+  <subitem
+        label="Select the Newton console.">
+        <action
+				pluginId="org.cauldron.sigil.help"
+				class="org.cauldron.sigil.cheatsheets.actions.ShowViewAction"
+				param1="org.eclipse.ui.console.ConsoleView"
+				translate="" />
+        
+  </subitem>
+  <subitem
+        label="Type &quot;sayhello&quot;. You should see the response:&lt;br/&gt;Hello World">
+  </subitem>	
+	</item>	
+	
+	<onCompletion>Congratulations you have created your first composite application using Sigil.</onCompletion>
+	
+	
+</cheatsheet>
diff --git a/sigil/org.cauldron.sigil.help/html/book.html b/sigil/org.cauldron.sigil.help/html/book.html
new file mode 100644
index 0000000..a9fc420
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/book.html
@@ -0,0 +1,45 @@
+<html>
+<header>
+<title>Sigil - Newton Development Plugin</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Sigil - Newton Development Plugin</h1></td><td align="right"><img src="images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<p>Sigil is an eclipse plugin that helps developers build and test code to be deployed on the Newton platform</p>
+
+<p>Features:<br/>
+<ul>
+    <li>New (Newton Project, Composite, System)</li>
+    <li>Syntax highlighting and code completion for SCA/System docs</li>
+    <li>Launch Newton in Debug/Standard vm from Eclipse</li>
+    <li>Full debug support - break points/source code/step through etc</li>
+    <li>Install Composites into running newton from Eclipse</li>
+    <li>Log integration</li>
+</ul>
+</p>
+
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/container_log.html b/sigil/org.cauldron.sigil.help/html/tasks/container_log.html
new file mode 100644
index 0000000..9b8808f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/container_log.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Viewing The Newton Log</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Viewing The Newton Log</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html
new file mode 100644
index 0000000..43db6da
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html
@@ -0,0 +1,66 @@
+<html>
+<header>
+<title>Debugging A Composite</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Debugging A Composite</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<p>In order to debug composite applications using the Newton Eclipse
+      plugin, use the runtime configuration you created in above, and start it
+      using a debug session. This will launch a newton container as a separate
+      process to the Eclipse IDE environment but which can be controlled from
+      the IDE</p>
+
+      
+<div align="center">
+<img class="figure" alt="Debug" src="my-images/debug.png"></div>
+      
+<p>Install your composite into this container using the Newton context
+      menu in the package view as documented above. In this example we have
+      installed the gui.composite component from the fractal render demo into
+      a Newton debug container.</p>
+
+      
+<div align="center">
+<img class="figure" alt="Fractal GUI Debug" src="my-images/demo-gui.png"></div>      
+
+<p>You can now add break points within your code to allow you to get a
+      better idea of what is going on within your application. In this example
+      we have added a break point to the FractalViewFrame of the fractal
+      render demo to break out into an interactive debug session when any of
+      the buttons <em>draw</em>, <em>save</em>, <em>cancel</em> are pressed on
+      the gui window.</p>
+
+      
+<div align="center">
+<img class="figure" alt="Add a breakpoint" src="my-images/breakpoint.png"></div>
+      
+<p>You can then inspect variable values in top-right-hand corner as per
+      normal debugging in the Eclipse IDE.</p>
+      
+<div align="center">
+<img class="figure" alt="Debug breakpoint" src="my-images/debug-breakpoint.png"></div>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html
new file mode 100644
index 0000000..e20d472
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html
@@ -0,0 +1,41 @@
+<html>
+<header>
+<title>Installing A Composite</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Installing A Composite</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<p>To install a composite into a Infiniflow Service Fabric container, you require a running
+      container instance on your machine. This can either be an external
+      process which you have launched using the bin/container script or the
+      container can be launched via an Eclipse runtime configuration as per
+      the instructions <a href="launch_newton.html" alt="Launching Newton">here</a>.</p> 
+      
+<p>You can then select your .composite file in the
+      package view and select Newton-&gt;Install Composite using the context
+      menu.</p>
+
+      
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2008 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html b/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html
new file mode 100644
index 0000000..6a2edc4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html
@@ -0,0 +1,55 @@
+<html>
+<header>
+<title>Launching Newton</title>
+</header>
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Launching Newton</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+	<h2>Runtime Configuration</h2>
+	<p>To run a Infiniflow Service Fabric container within Eclipse you first need to configure
+      its runtime options in the IDE.</p>
+      
+	<p>Open the Run configuration dialog by selecting the Run &rarr; Run&hellip; option
+      from the menu bar.</p>
+
+	<div align="center"><img class="figure" alt="Run Menu" src="my-images/run-menu.png"></div>
+
+	<p>Create a new Newton configuration by right-clicking on the Newton
+      configuration group and selecting <em>New</em>. Type in a name for your
+      configuration, the other default options should be sufficient for most
+      users.</p>
+
+      
+	<div align="center"><img class="figure" alt="Run Dialog" src="my-images/run.png"></div>
+
+	<h2>Launch Newton</h2>      
+	<p>Click the <em>Run</em> button to start the container, you will see
+      the container output appear in the console tab. Wait until the <em>Boot
+      Complete</em> line appears, as below.</p>
+
+      
+	<div align="center"><img class="figure" alt="Boot Complete" src="my-images/run-complete.png"></div>
+        
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html
new file mode 100644
index 0000000..58d9541
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html
@@ -0,0 +1,41 @@
+<html>
+<header>
+<title>Creating SCA Composites</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Creating SCA Composites</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+	<p>
+	Create a new sca composite document using the <em>New->Other</em> menu option.<br/>
+	Page 1:<br/>
+	<img src="../images/new_composite_page1.png" alt="New Composite Page 1" /><br/>
+	Page 2:<br/>
+	<img src="../images/new_composite_page2.png" alt="New Composite Page 2" />
+	</p>
+	<p>There is basic support for context sensitive text completion in sca documents. Press <em>Ctrl+Space</em> to see a list of valid elements at the current cursor location.<br/>
+	<img src="../images/composite_suggest_text.png" alt="New Composite Page 2" />
+	</p>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_project.html b/sigil/org.cauldron.sigil.help/html/tasks/new_project.html
new file mode 100644
index 0000000..90e14e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_project.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Creating A Newton Project</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Creating A Newton Project</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_system.html b/sigil/org.cauldron.sigil.help/html/tasks/new_system.html
new file mode 100644
index 0000000..a0cc7ae
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_system.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Creating Newton Systems</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Creating Newton Systems</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html b/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html
new file mode 100644
index 0000000..8334f70
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html
@@ -0,0 +1,32 @@
+<html>
+<header>
+<title>Configuring Sigil</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+	<tr>
+		<td valign="bottom"><h1>Configuring Sigil</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+	</tr>
+	<tr><td colspan="2">
+<p>After having installed sigil, the preferences dialog should appear and ask you to specify the location of a valid newton install directory.</p>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+          Copyright &copy; 2003-2007 Paremus Limited.</a>
+      All rights reserved.
+            <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+              document.write(" - "+"Last Published: " + document.lastModified);
+              //  --></script>
+<div id="feedback">
+    Send feedback about the website to:
+  <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/plugin.xml b/sigil/org.cauldron.sigil.help/plugin.xml
new file mode 100644
index 0000000..0586903
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/plugin.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+   <extension
+         point="org.eclipse.ui.cheatsheets.cheatSheetContent">
+      <category
+            id="org.cauldron.sigil.help.category"
+            name="Sigil">
+      </category>
+      <cheatsheet
+            category="org.cauldron.sigil.help.category"
+            composite="false"
+            contentFile="cheatsheets/HelloWorld.xml"
+            id="org.cauldron.sigil.help.createProjectCheatSheet"
+            name="Create a Hello World composite application">
+      </cheatsheet>
+   </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java
new file mode 100644
index 0000000..5875264
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+public abstract class AbstractNewWizardAction extends Action {
+
+	@Override
+	public void run() {
+		Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+		
+		try {
+			INewWizard wizard = createWizard();
+			wizard.init(PlatformUI.getWorkbench(), getSelection());
+			WizardDialog dialog = new WizardDialog(shell, wizard);
+			int res = dialog.open();
+			notifyResult(res == Window.OK);
+		} catch (CoreException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}		
+	}
+	
+	protected abstract INewWizard createWizard() throws CoreException;
+
+	private IStructuredSelection getSelection() {
+		IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+		if (window != null)
+		{
+			ISelection selection = window.getSelectionService().getSelection();
+			if (selection instanceof IStructuredSelection)
+			{
+				return (IStructuredSelection)selection;
+			}
+		}
+		return StructuredSelection.EMPTY;
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java
new file mode 100644
index 0000000..81cdccd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+import org.eclipse.ui.part.FileEditorInput;
+import org.osgi.framework.Bundle;
+
+public class CopyResourceFromPlugin extends Action implements ICheatSheetAction {
+
+	private String targetProject;
+	private String targetFolder;
+	private String sourceBundle;
+	private String sourcePath;
+	private String editorID;
+	
+	public void run(String[] params, ICheatSheetManager manager) {
+		if ( params != null && params.length > 4 )  {
+			targetProject = params[0];
+			targetFolder = params[1];
+			sourceBundle= params[2];
+			sourcePath = params[3];
+			editorID = params[4];
+		}
+		
+		WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+			@Override
+			protected void execute(IProgressMonitor monitor) throws CoreException {
+				try {
+					Bundle b = Platform.getBundle(sourceBundle);
+					
+				    IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+				    IProject project = workspaceRoot.getProject(targetProject);
+				    IPath path = new Path( targetFolder ).append( sourcePath.substring( sourcePath.lastIndexOf( '/' ) ) );
+					IFile file = project.getFile( path );
+					
+					if ( !file.exists() ) {
+						mkdirs( (IFolder) file.getParent(), monitor );
+						
+						InputStream in = FileLocator.openStream(b, new Path(sourcePath), false);
+						file.create(in, true, monitor);
+					}
+					
+					IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+					FileEditorInput input = new FileEditorInput(file); 
+					window.getActivePage().openEditor(input, editorID);
+				} catch (IOException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		};
+		
+		try {
+			new ProgressMonitorDialog(Display.getCurrent().getActiveShell()).run(false, false, op);
+		} catch (InvocationTargetException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private void mkdirs(IFolder folder, IProgressMonitor monitor) throws CoreException {
+		IContainer parent = folder.getParent();
+		if ( !parent.exists() ) {
+			mkdirs((IFolder) parent, monitor);			
+		}
+		
+		if ( !folder.exists() ) {
+			folder.create(true, true, monitor);
+		}
+		
+	}		
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java
new file mode 100644
index 0000000..34d08da
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.ui.wizard.project.SigilProjectWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class OpenEmptySigilProjectWizardAction extends AbstractNewWizardAction implements ICheatSheetAction {
+
+	private String name;
+	
+	public void run(String[] params, ICheatSheetManager manager) {
+		if ( params != null && params.length > 0 )  {
+			name = params[0];
+		}
+		
+		run();
+	}
+
+	@Override
+	protected INewWizard createWizard() throws CoreException {
+		SigilProjectWizard wizard = new SigilProjectWizard();
+		wizard.setName(name);
+		return wizard;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java
new file mode 100644
index 0000000..a851e3d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.actions.ResolveProjectDependenciesAction;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class ResolveProjectDependencies extends Action implements ICheatSheetAction {
+
+	private String targetProject;
+	
+	public void run(String[] params, ICheatSheetManager manager) {
+		if ( params != null && params.length > 3 )  {
+			targetProject = params[0];
+		}
+
+	    IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+	    IProject project = workspaceRoot.getProject(targetProject);
+	    
+	    try {
+			ISigilProjectModel sigil = SigilCore.create(project);
+			new ResolveProjectDependenciesAction(sigil, false).run();
+		} catch (CoreException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java
new file mode 100644
index 0000000..ef48890
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class ShowViewAction extends Action implements ICheatSheetAction {
+
+	public void run(String[] params, ICheatSheetManager manager) {
+		if ( params != null && params.length > 0 )  {
+			IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+			IWorkbenchPage page = window.getActivePage();
+			try {
+				page.showView(params[0]);
+			} catch (PartInitException e) {
+				SigilCore.error( "Failed to show view", e);
+			}
+		}
+
+	}
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java
new file mode 100644
index 0000000..8a94528
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.help;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.help";
+
+	// The shared instance
+	private static Activator plugin;
+	
+	/**
+	 * The constructor
+	 */
+	public Activator() {
+	}
+
+	/*
+	 * (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;
+	}
+
+	/*
+	 * (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);
+	}
+
+	/**
+	 * Returns the shared instance
+	 *
+	 * @return the shared instance
+	 */
+	public static Activator getDefault() {
+		return plugin;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.help/toc.xml b/sigil/org.cauldron.sigil.help/toc.xml
new file mode 100644
index 0000000..191daf4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/toc.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<toc label="Sigil - Newton Development Plugin" topic="html/book.html">
+	<topic label="Configuring Sigil" href="html/tasks/sigil_config.html"/>
+	<topic label="Creating A Newton Project" href="html/tasks/new_project.html"/> 
+	<topic label="Creating SCA Composites" href="html/tasks/new_composite.html"/>
+	<topic label="Creating Newton Systems" href="html/tasks/new_system.html"/>
+	<topic label="Launching Newton" href="html/tasks/launch_newton.html"/>
+	<topic label="Viewing The Newton Log" href="html/tasks/container_log.html"/>
+	<topic label="Installing Your Composites" href="html/tasks/install_composite.html"/>	
+	<topic label="Debugging Your Composites" href="html/tasks/debug_composite.html"/>	
+</toc>
diff --git a/sigil/org.cauldron.sigil.junit/.classpath b/sigil/org.cauldron.sigil.junit/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.junit/.project b/sigil/org.cauldron.sigil.junit/.project
new file mode 100644
index 0000000..30c4b02
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.junit</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1108409
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Junit Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.junit;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.junit.activator.Activator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.cauldron.sigil.core;bundle-version="0.7.0",
+ org.apache.ant;bundle-version="1.7.0",
+ org.junit4;bundle-version="4.3.1",
+ org.junit4.source;bundle-version="4.3.1";resolution:=optional
diff --git a/sigil/org.cauldron.sigil.junit/build.properties b/sigil/org.cauldron.sigil.junit/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml
diff --git a/sigil/org.cauldron.sigil.junit/plugin.xml b/sigil/org.cauldron.sigil.junit/plugin.xml
new file mode 100644
index 0000000..99f4abc
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/plugin.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java b/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java
new file mode 100644
index 0000000..b9bbc32
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.activator;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.junit";
+
+	// The shared instance
+	private static Activator plugin;
+	
+	/**
+	 * The constructor
+	 */
+	public Activator() {
+	}
+
+	/*
+	 * (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;
+	}
+
+	/*
+	 * (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);
+	}
+
+	/**
+	 * Returns the shared instance
+	 *
+	 * @return the shared instance
+	 */
+	public static Activator getDefault() {
+		return plugin;
+	}
+
+	public static void runTests(ISigilProjectModel project) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.obr.feature/.project b/sigil/org.cauldron.sigil.obr.feature/.project
new file mode 100644
index 0000000..cc440e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.obr.feature</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.FeatureBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.FeatureNature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.obr.feature/build.properties b/sigil/org.cauldron.sigil.obr.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/sigil/org.cauldron.sigil.obr.feature/feature.xml b/sigil/org.cauldron.sigil.obr.feature/feature.xml
new file mode 100644
index 0000000..c7d8f45
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/feature.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<feature
+      id="org.cauldron.sigil.obr.feature"
+      label="Sigil OBR"
+      version="0.8.0.qualifier"
+      provider-name="Paremus Ltd">
+
+   <description>
+      Sigil OBR plugin, allows Sigil to use OSGi Bundle Repositories to download OSGi bundles into the Eclipse workspace.
+   </description>
+
+   <copyright>
+      Copyright (C) 2003-2008 Paremus Limited, London, United Kingdom
+   </copyright>
+
+   <license url="http://www.apache.org/licenses/LICENSE-2.0">
+      Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+   </license>
+
+   <url>
+      <update url="http://replace.with.real.url"/>
+   </url>
+
+   <requires>
+      <import plugin="org.eclipse.ui"/>
+      <import plugin="org.eclipse.core.runtime"/>
+      <import plugin="org.cauldron.sigil.core" version="0.6.0" match="greaterOrEqual"/>
+      <import plugin="org.cauldron.sigil.ui" version="0.6.0" match="greaterOrEqual"/>
+      <import plugin="org.cauldron.bld.core" version="0.6.0" match="greaterOrEqual"/>
+      <import plugin="org.eclipse.osgi"/>
+      <import plugin="org.eclipse.equinox.common" version="3.4.0" match="greaterOrEqual"/>
+   </requires>
+
+   <plugin
+         id="org.cauldron.sigil.obr"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+   <plugin
+         id="org.cauldron.bld.obr"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.obr/.classpath b/sigil/org.cauldron.sigil.obr/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.obr/.project b/sigil/org.cauldron.sigil.obr/.project
new file mode 100644
index 0000000..7990e04
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.obr</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..902de9d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Wed Aug 27 13:25:05 BST 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9a09325
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: OBR Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.obr;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.obr.Activator
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.cauldron.sigil.core;bundle-version="0.6.0",
+ org.cauldron.sigil.ui;bundle-version="0.6.0",
+ org.cauldron.bld.core
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+Import-Package: org.cauldron.bld.obr,
+ org.cauldron.sigil.model
diff --git a/sigil/org.cauldron.sigil.obr/build.properties b/sigil/org.cauldron.sigil.obr/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml
diff --git a/sigil/org.cauldron.sigil.obr/plugin.xml b/sigil/org.cauldron.sigil.obr/plugin.xml
new file mode 100644
index 0000000..03b79c1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/plugin.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+   <extension
+         point="org.cauldron.sigil.repositoryprovider">
+      <provider
+            class="org.cauldron.bld.obr.OBRRepositoryProvider"
+            dynamic="true"
+            id="org.cauldron.sigil.obr.provider"
+            type="OSGi Bundle Repository (OBR)">
+      </provider>
+   </extension>
+   <extension
+         point="org.cauldron.sigil.ui.repositorywizard">
+      <wizard
+            class="org.cauldron.sigil.obr.OBRRepositoryWizard"
+            repository="org.cauldron.sigil.obr.provider">
+      </wizard>
+   </extension>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java
new file mode 100644
index 0000000..4bad94b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.obr";
+
+	// The shared instance
+	private static Activator plugin;
+	
+	/**
+	 * The constructor
+	 */
+	public Activator() {
+	}
+
+	/*
+	 * (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;
+	}
+
+	/*
+	 * (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);
+	}
+
+	/**
+	 * Returns the shared instance
+	 *
+	 * @return the shared instance
+	 */
+	public static Activator getDefault() {
+		return plugin;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java
new file mode 100644
index 0000000..79e0e72
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class OBRRepositoryWizard extends RepositoryWizard {
+	
+	@Override
+	public void addPages() {
+		addPage( new OBRRepositoryWizardPage(this) );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java
new file mode 100644
index 0000000..a607ac1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+
+public class OBRRepositoryWizardPage extends RepositoryWizardPage implements IWizardPage {
+
+	private StringFieldEditor urlEditor;
+	private StringFieldEditor cacheEditor;
+
+	protected OBRRepositoryWizardPage(RepositoryWizard parent) {
+		super("OSGi Bundle Repository", parent);
+	}
+
+	@Override
+	public void createFieldEditors() {
+		createField( urlEditor = new StringFieldEditor("url", "URL:", getFieldEditorParent()) );
+		createField( cacheEditor = new DirectoryFieldEditor("cache", "Cache:", getFieldEditorParent()) );
+		addField( new BooleanFieldEditor( "inmemory", "In Memory:", getFieldEditorParent() ));
+	}
+
+	private void createField(StringFieldEditor editor) {
+		editor.getTextControl(getFieldEditorParent()).addModifyListener( new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				checkPageComplete();
+			}
+		});
+		addField(editor);
+	}
+
+	@Override
+	protected void checkPageComplete() {
+		super.checkPageComplete();
+		if ( isPageComplete() && checkURLComplete() ) {
+			checkCacheComplete();
+		}
+	}
+
+	private boolean checkCacheComplete() {
+		setPageComplete(cacheEditor.getStringValue().length() > 0);
+		
+		if ( isPageComplete() ) {
+			if ( new File( cacheEditor.getStringValue() ).isDirectory() ) {
+				setErrorMessage(null);				
+			}
+			else {
+				setErrorMessage("Invalid cache directory");
+				setPageComplete(false);
+			}
+		}
+		
+		return isPageComplete();
+	}
+
+	private boolean checkURLComplete() {
+		setPageComplete(urlEditor.getStringValue().length() > 0);
+		
+		if ( isPageComplete() ) {
+			try {
+				new URL(urlEditor.getStringValue());
+				setErrorMessage(null);
+			}
+			catch (MalformedURLException e) {
+				if ( !new File(urlEditor.getStringValue()).isFile() ) {
+					setErrorMessage("Invalid repository url: " + e.getMessage());
+					setPageComplete(false);
+				}
+			}
+		}
+		
+		return isPageComplete();
+	}
+
+	@Override
+	public void storeFields() {
+		super.storeFields();
+		IPath dir = Activator.getDefault().getStateLocation();
+		getModel().getPreferences().setValue( "index", dir.append( getModel().getId() + ".obr" ).toOSString() );
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.search/.classpath b/sigil/org.cauldron.sigil.search/.classpath
new file mode 100644
index 0000000..254ffb7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry exported="true" kind="lib" path="lib/bcel-5.2.jar"/>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.search/.project b/sigil/org.cauldron.sigil.search/.project
new file mode 100644
index 0000000..3e37565
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.search</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..636afaf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Fri Oct 03 18:13:19 PDT 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7a97ca1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Search Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.search;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.search.SigilSearch
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.jdt.core;bundle-version="3.4.0",
+ org.cauldron.sigil.core;bundle-version="0.7.0",
+ org.cauldron.bld.core;bundle-version="0.7.0",
+ org.eclipse.core.resources;bundle-version="3.4.0"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.cauldron.sigil.search
+Bundle-ClassPath: lib/bcel-5.2.jar,
+ .
diff --git a/sigil/org.cauldron.sigil.search/build.properties b/sigil/org.cauldron.sigil.search/build.properties
new file mode 100644
index 0000000..ef5e456
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               lib/bcel-5.2.jar
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java
new file mode 100644
index 0000000..f47deed
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+
+public interface ISearchResult {
+	ISigilBundle getProvider();
+	IPackageExport getExport();
+	String getPackageName();
+	String getClassName();
+}
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java
new file mode 100644
index 0000000..79ca623
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Pattern;
+
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.cauldron.sigil.search.index.Index;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilSearch extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.search";
+
+	private static final String CLASS_EXTENSION = ".class";
+
+	// The shared instance
+	private static SigilSearch plugin;
+	private static Index index;
+
+	/**
+	 * The constructor
+	 */
+	public SigilSearch() {
+	}
+	
+	public static List<ISearchResult> findProviders(String fullyQualifiedName, ISigilProjectModel sigil, IProgressMonitor monitor) {
+		listen(sigil);
+		return index.findProviders(fullyQualifiedName, monitor);
+	}
+	
+	public static List<ISearchResult> findProviders(Pattern namePattern, ISigilProjectModel sigil, IProgressMonitor monitor) {
+		listen(sigil);
+		return index.findProviders(namePattern, monitor);
+	}
+	
+	/*
+	 * (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;
+	}
+
+	/*
+	 * (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);
+	}
+
+	/**
+	 * Returns the shared instance
+	 *
+	 * @return the shared instance
+	 */
+	public static SigilSearch getDefault() {
+		return plugin;
+	}
+	
+	private static void listen(ISigilProjectModel sigil) {
+		synchronized(plugin) {
+			if ( index == null ) {
+				index = new Index();
+				for ( IBundleRepository rep : SigilCore.getRepositoryManager(sigil).getRepositories() ) {
+					index(index, rep);
+				}
+				
+				SigilCore.getRepositoryManager(sigil).addRepositoryChangeListener( new IRepositoryChangeListener() {
+					public void repositoryChanged(RepositoryChangeEvent event) {
+						index(index, event.getRepository());
+					}					
+				});
+			}
+		}
+	}
+
+	private static void index(final Index index, final IBundleRepository rep) {
+		index.delete(rep);
+		rep.accept( new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class);
+				if ( p == null ) {
+					if ( bundle.isSynchronized() ) {
+						IPath loc = bundle.getLocation();
+						if ( loc.isAbsolute() ) {
+							indexJar(rep, bundle, loc);
+						}
+					}
+				}
+				else {
+					indexProject(rep, p);
+				}
+				return true;
+			}					
+		});
+	}
+	
+	private static void indexProject(IBundleRepository rep, ISigilProjectModel sigil) {
+		try {
+			for (ICompilationUnit unit : JavaHelper.findCompilationUnits(sigil) ) {
+				IPackageFragment p = (IPackageFragment) unit.getParent();
+				ISigilBundle b = sigil.getBundle();
+				IPackageExport export = b.findExport(p.getElementName());
+				index.addEntry(unit, rep, b, export != null);
+			}
+		} catch (JavaModelException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	private static void indexJar(IBundleRepository rep, ISigilBundle bundle, IPath loc) {
+		JarFile jar = null;
+		try {
+			jar = new JarFile(loc.toOSString());
+			for (Map.Entry<JarEntry, IPackageExport> export : findExportedClasses(bundle, jar).entrySet() ) {
+				JarEntry entry = export.getKey();
+				InputStream in = null;
+				try {
+					in = jar.getInputStream(entry);
+					ClassParser parser = new ClassParser(in, entry.getName());
+					JavaClass c = parser.parse();
+					index.addEntry(c, rep, bundle, true);
+				}
+				finally {
+					if ( in != null ) {
+						in.close();
+					}
+				}
+			}
+		}
+		catch (IOException e) {
+			SigilCore.error( "Failed to read jar " + loc, e );
+		}
+		finally {
+			if ( jar != null ) {
+				try {
+					jar.close();
+				} catch (IOException e) {
+					SigilCore.error( "Failed to close jar " + loc, e );
+				}
+			}
+		}
+	}
+
+	private static Map<JarEntry, IPackageExport> findExportedClasses(ISigilBundle bundle, JarFile jar) {
+		HashMap<JarEntry, IPackageExport> found = new HashMap<JarEntry, IPackageExport>();
+		
+		IPackageExport[] exports = bundle.getBundleInfo().childrenOfType(IPackageExport.class);
+		if ( exports.length > 0 ) {
+			Arrays.sort(exports, new Comparator<IPackageExport> () {
+				public int compare(IPackageExport o1, IPackageExport o2) {
+					return -1 * o1.compareTo(o2);
+				}				
+			});
+			for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements();) {
+				JarEntry entry = e.nextElement();
+				String className = toClassName(entry);
+				if ( className != null ) {
+					IPackageExport ex = findExport(className, exports);
+					
+					if ( found != null ) {
+						found.put( entry, ex );
+					}
+				}
+			}
+		}
+		
+		return found;
+	}
+
+	private static IPackageExport findExport(String className, IPackageExport[] exports) {
+		for ( IPackageExport e : exports ) {
+			if ( className.startsWith(e.getPackageName())) {
+				return e;
+			}
+		}
+		return null;
+	}
+
+	private static String toClassName(JarEntry entry) {
+		String name = entry.getName();
+		if ( name.endsWith(CLASS_EXTENSION) ) {
+			name = name.substring(0, name.length() - CLASS_EXTENSION.length());
+			name = name.replace('/', '.');
+			return name;
+		}
+		else {
+			return null;
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java
new file mode 100644
index 0000000..b62421f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search.index;
+
+import java.lang.ref.SoftReference;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Pattern;
+
+import org.apache.bcel.classfile.JavaClass;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.search.ISearchResult;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.osgi.framework.Version;
+
+public class Index {
+	private HashMap<String, ClassData> primary = new HashMap<String, ClassData>();
+	private HashMap<IBundleRepository, HashSet<String>> secondary = new HashMap<IBundleRepository, HashSet<String>>();
+	
+	private final ReadWriteLock lock = new ReentrantReadWriteLock();
+	
+	static class ClassData {
+		HashMap<IBundleRepository, Set<ISearchResult>> provided = new HashMap<IBundleRepository, Set<ISearchResult>>();
+
+		void add(IBundleRepository rep, ISearchResult export) {
+			Set<ISearchResult> exports = provided.get(rep);
+			
+			if ( exports == null ) {
+				exports = new HashSet<ISearchResult>();
+				provided.put( rep, exports );
+			}
+			
+			exports.add(export);
+		}
+
+		List<ISearchResult> getResults() {
+			LinkedList<ISearchResult> exports = new LinkedList<ISearchResult>();
+			for ( Set<ISearchResult> p : provided.values() ) {
+				exports.addAll(p);
+			}
+			return exports;
+		}
+
+		void remove(IBundleRepository rep) {
+			provided.remove(rep);
+		}
+		
+		boolean isEmpty() {
+			return provided.isEmpty();
+		}
+	}
+	
+	static class SearchResult implements ISearchResult {
+		private final String className;
+		private final String packageName;
+		private final IBundleRepository rep;
+		private final String bundleSymbolicName;
+		private final Version version;
+		private final boolean exported;
+		
+		private SoftReference<ISigilBundle> bundleReference;
+		private SoftReference<IPackageExport> exportReference;
+		
+		public SearchResult(String className, IBundleRepository rep, ISigilBundle bundle, String packageName, boolean exported) {
+			this.className = className;
+			this.rep = rep;
+			this.exported = exported;
+			this.bundleSymbolicName = bundle.getBundleInfo().getSymbolicName();
+			this.version = bundle.getVersion();
+			this.packageName = packageName;
+		}
+
+		public String getClassName() {
+			return className;
+		}
+		
+		public String getPackageName() {
+			return packageName;
+		}
+
+		public IPackageExport getExport() {
+			IPackageExport ipe = null;
+			if ( exported ) {
+				ipe = exportReference == null ? null : exportReference.get();
+				if (ipe == null) {
+					ipe = getProvider().findExport(packageName);
+					exportReference = new SoftReference<IPackageExport>(ipe);
+				}
+			}
+			return ipe; 
+		}
+
+		public ISigilBundle getProvider() {
+			ISigilBundle b = bundleReference == null ? null : bundleReference.get();
+			if ( b == null ) {
+				IRequiredBundle rb = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+				rb.setSymbolicName(bundleSymbolicName);
+				VersionRange versions = new VersionRange(false, version, version, false);
+				rb.setVersions(versions);
+				b = rep.findProvider(rb, 0);
+				bundleReference = new SoftReference<ISigilBundle>(b);
+			}
+			return b;
+		}
+		
+	}
+
+	public void addEntry(JavaClass c, IBundleRepository rep, ISigilBundle bundle, boolean exported) {
+		addEntry(c.getClassName(), rep, bundle, c.getPackageName(), exported);
+	}
+	
+	public void addEntry(ICompilationUnit unit, IBundleRepository rep, ISigilBundle bundle, boolean exported) {
+		String name = unit.getElementName();
+		if ( name.endsWith( ".java" ) ) {
+			name = name.substring(0, name.length() - 5 );
+		}
+		IPackageFragment p = (IPackageFragment) unit.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
+		addEntry(p.getElementName() + "." + name, rep, bundle, p.getElementName(), exported);
+	}
+
+	private void addEntry(String className, IBundleRepository rep, ISigilBundle bundle, String packageName, boolean exported) {
+		List<String> keys = genKeys(className);
+		lock.writeLock().lock();
+		try {
+			for ( String key : keys ) {
+				ClassData data = primary.get(key);
+				
+				if ( data == null ) {
+					data = new ClassData();
+					primary.put(key, data);
+				}
+				
+				SearchResult result = new SearchResult(className, rep, bundle, packageName, exported);
+				data.add(rep, result);
+			}
+			
+			HashSet<String> all = secondary.get(rep);
+			if ( all == null ) {
+				all = new HashSet<String>();
+				secondary.put(rep, all);
+			}
+			all.addAll(keys);
+		}
+		finally {
+			lock.writeLock().unlock();
+		}
+	}
+
+	
+	public List<ISearchResult> findProviders(String className, IProgressMonitor monitor) {
+		lock.readLock().lock();
+		try {
+			ClassData data = primary.get(className);
+			return data == null ? Collections.<ISearchResult>emptyList() : data.getResults();
+		}
+		finally {
+			lock.readLock().unlock();
+		}
+	}	
+	
+	public List<ISearchResult> findProviders(Pattern className, IProgressMonitor monitor) {
+		lock.readLock().lock();
+		try {
+			ClassData data = primary.get(className);
+			return data == null ? Collections.<ISearchResult>emptyList() : data.getResults();
+		}
+		finally {
+			lock.readLock().unlock();
+		}
+	}
+	
+	public void delete(IBundleRepository rep) {
+		lock.writeLock().lock();
+		try {
+			Set<String> keys = secondary.remove(rep);
+			if ( keys != null ) {
+				for ( String key : keys ) {
+					ClassData data = primary.get(key);
+					data.remove(rep);
+					if ( data.isEmpty() ) {
+						primary.remove(key);
+					}
+				}
+			}
+		}
+		finally {
+			lock.writeLock().unlock();
+		}
+	}
+	
+	private List<String> genKeys(String className) {
+		LinkedList<String> keys = new LinkedList<String>();
+		keys.add(className);
+		int i = className.lastIndexOf('.');
+		if ( i != -1 ) {
+			String name = className.substring(i + 1);
+			keys.add( name );
+		}
+		return keys;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/.classpath b/sigil/org.cauldron.sigil.ui/.classpath
new file mode 100644
index 0000000..6f93ad7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+		<accessrules>
+			<accessrule kind="accessible" pattern="org/eclipse/pde/internal/ui/parts/*"/>
+		</accessrules>
+	</classpathentry>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.ui/.project b/sigil/org.cauldron.sigil.ui/.project
new file mode 100644
index 0000000..e736268
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.ui</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4b4103a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Ui Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.ui;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.ui.SigilUI
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.cauldron.sigil.core,
+ org.cauldron.bld.core,
+ org.cauldron.sigil.utils,
+ org.cauldron.sigil.search,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.pde.ui,
+ org.eclipse.ui,
+ org.eclipse.ui.editors,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.help,
+ org.eclipse.zest.core;bundle-version="[1,2)",
+ org.eclipse.zest.layouts;bundle-version="[1,2)",
+ org.eclipse.ltk.core.refactoring
+Eclipse-LazyStart: true
+Export-Package: org.cauldron.sigil.actions,
+ org.cauldron.sigil.handlers,
+ org.cauldron.sigil.ui,
+ org.cauldron.sigil.ui.util,
+ org.cauldron.sigil.ui.wizard.project,
+ org.cauldron.sigil.ui.wizard.repository
+Bundle-Localization: plugin
+
diff --git a/sigil/org.cauldron.sigil.ui/build.properties b/sigil/org.cauldron.sigil.ui/build.properties
new file mode 100644
index 0000000..df5bef4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/build.properties
@@ -0,0 +1,12 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               etc/,\
+               plugin.xml,\
+               icons/,\
+               resources/,\
+               toc.xml,\
+               html/,\
+               plugin.properties,\
+               schema/
diff --git a/sigil/org.cauldron.sigil.ui/plugin.properties b/sigil/org.cauldron.sigil.ui/plugin.properties
new file mode 100644
index 0000000..3e6d20d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.properties
@@ -0,0 +1,5 @@
+repositoriesPrefPage=Repositories
+newtonRepoPrefs=Newton Repository
+librariesPrefsPage=OSGi Libraries
+commandConvertProject=Convert Project To Sigil Project
+commandRefreshClasspath=Refresh bundle classpath
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/plugin.xml b/sigil/org.cauldron.sigil.ui/plugin.xml
new file mode 100644
index 0000000..d8b96a5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+   <extension-point id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard" schema="schema/org.cauldron.sigil.ui.repositorywizard.exsd"/>
+      <extension
+         point="org.eclipse.ui.startup">
+      <startup class="org.cauldron.sigil.startup.SigilStartup"/>
+   </extension>
+   <extension
+         point="org.eclipse.ui.newWizards">
+      <category
+            id="org.cauldron.sigil.newWizardCategory"
+            name="Sigil"/>
+      <wizard
+            category="org.cauldron.sigil.newWizardCategory"
+            class="org.cauldron.sigil.ui.wizard.project.SigilProjectWizard"
+            finalPerspective="org.cauldron.sigil.ui.perspective"
+            icon="etc/images/newton.png"
+            id="org.cauldron.sigil.editors.newProjectWizard"
+            name="Sigil Project"
+            project="true"/>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            class="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            id="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            name="Sigil"/>
+      <!--page
+            category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            class="org.cauldron.sigil.ui.preferences.LibraryPreferencePage"
+            id="org.cauldron.sigil.ui.libraryPreferences"
+            name="%librariesPrefsPage" >
+      </page-->
+      <page
+            category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            class="org.cauldron.sigil.ui.preferences.repository.RepositoriesPreferencePage"
+            id="org.cauldron.sigil.ui.preferences.repositoriesPreferencePage"
+            name="%repositoriesPrefPage">
+      </page>
+      <page
+            category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            class="org.cauldron.sigil.ui.preferences.ExcludedResourcesPrefsPage"
+            id="org.cauldron.sigil.ui.preferences.excludedResources"
+            name="Excluded Resources">
+      </page>
+      <page
+            category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            class="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+            id="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+            name="Version Handling">
+      </page>
+      <page
+            category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+            class="org.cauldron.sigil.ui.preferences.installs.OSGiInstallsPreferencePage"
+            id="org.cauldron.sigil.ui.preferences.newtonInstalls"
+            name="Newton Installs">
+      </page>
+   </extension>
+   <extension
+         point="org.eclipse.ui.editors">
+      <editor
+            class="org.cauldron.sigil.ui.editors.project.SigilProjectEditorPart"
+            default="true"
+            filenames="sigil.properties"
+            icon="etc/images/newton.png"
+            id="org.cauldron.sigil.editors.SigilProjectEditor"
+            name="Sigil Project Editor"/>
+   </extension>
+   <extension
+         point="org.eclipse.jdt.core.classpathContainerInitializer">
+      <classpathContainerInitializer
+            class="org.cauldron.sigil.classpath.SigilClasspathContainerInitializer"
+            id="org.cauldron.sigil.core.classpathContainer"/>
+   </extension>
+   <extension
+         point="org.eclipse.jdt.ui.classpathContainerPage">
+      <classpathContainerPage
+            class="org.cauldron.sigil.classpath.SigilLibraryPage"        id="org.cauldron.sigil.core.classpathContainer"
+            name="Sigil Library"/>
+   </extension>
+   <extension
+         point="org.eclipse.ui.perspectives">
+      <perspective
+            class="org.cauldron.sigil.ui.perspective.SigilPerspectiveFactory"
+            icon="etc/images/newton.png"
+            id="org.cauldron.sigil.ui.perspective"
+            name="Sigil">
+      </perspective>
+   </extension>
+   <extension
+         point="org.eclipse.jdt.ui.quickFixProcessors">
+      <quickFixProcessor
+            class="org.cauldron.sigil.ui.quickfix.ImportQuickFixProcessor"
+            id="org.cauldron.sigil.ui.quickFixProcessor1">
+      </quickFixProcessor>
+   </extension>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            allowMultiple="false"
+            category="org.cauldron.sigil.ui.views"
+            class="org.cauldron.sigil.ui.views.resolution.BundleResolverView"
+            icon="icons/jar_web_obj.png"
+            id="org.cauldron.sigil.ui.bundleDependencyView"
+            name="Bundle Dependency View"
+            restorable="true">
+      </view>
+      <view
+            allowMultiple="false"
+            category="org.cauldron.sigil.ui.views"
+            class="org.cauldron.sigil.ui.views.RepositoryViewPart"
+            icon="icons/jars_obj.png"
+            id="org.cauldron.sigil.ui.repositoryBrowser"
+            name="Repository Browser"
+            restorable="true">
+      </view>
+      <category
+            id="org.cauldron.sigil.ui.views"
+            name="Sigil">
+      </category>
+   </extension>
+   <extension
+         point="org.cauldron.sigil.ui.repositorywizard">
+      <wizard
+            class="org.cauldron.sigil.ui.internal.repository.FileSystemRepositoryWizard"
+            repository="org.cauldron.sigil.core.file">
+      </wizard>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commands">
+      <command
+            id="org.cauldron.sigil.ui.commands.renameComposite"
+            name="Rename Composite">
+      </command>
+   </extension>
+   <extension
+         point="org.eclipse.ltk.core.refactoring.refactoringContributions">
+      <contribution
+            class="org.cauldron.sigil.ui.refactor.RenameCompositeRefactoring"
+            id="org.cauldron.sigil.ui.rename.composite">
+      </contribution>
+   </extension>
+   <extension
+         point="org.eclipse.ui.propertyPages">
+      <page
+            class="org.cauldron.sigil.ui.preferences.project.ProjectPropertyPage"
+            id="org.cauldron.sigil.ui.projectpage"
+            name="Repositories">
+         <filter
+               name="projectNature"
+               value="org.cauldron.sigil.core.newtonnature"/>
+         <enabledWhen>
+            <adapt
+                  type="org.eclipse.core.resources.IProject">
+            </adapt>
+         </enabledWhen>
+      </page>
+   </extension>
+   <extension
+         point="org.eclipse.ui.menus">
+      <menuContribution
+            locationURI="popup:org.eclipse.ui.popup.any">
+         <menu
+               icon="etc/images/newton.png"
+               label="Sigil">
+            <command
+                  commandId="org.cauldron.sigil.ui.convertproject"
+                  label="Convert Project"
+                  style="push">
+               <visibleWhen
+                     checkEnabled="true">
+               </visibleWhen>
+            </command>
+         </menu>
+         <separator
+               name="org.cauldron.sigil.ui.separator"
+               visible="true">
+         </separator>
+         <command
+               commandId="org.cauldron.sigil.ui.refreshclasspath"
+               icon="icons/refreshBundle.png"
+               label="Refresh bundle classpath"
+               style="push">
+               <visibleWhen
+                     checkEnabled="true">
+               </visibleWhen>
+            </command>
+      </menuContribution>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commands">
+      <command
+            id="org.cauldron.sigil.ui.convertproject"
+            name="%commandConvertProject">
+      </command>
+      <command
+            id="org.cauldron.sigil.ui.refreshclasspath"
+            name="%commandRefreshClasspath">
+      </command>
+   </extension>
+   <extension
+         point="org.eclipse.ui.commandImages">
+      <image
+            commandId="org.cauldron.sigil.ui.convertproject"
+            icon="etc/images/newton.png">
+      </image>
+      <image
+            commandId="org.cauldron.sigil.ui.refreshclasspath"
+            icon="etc/images/newton.png">
+      </image>
+   </extension>
+   
+   <extension
+         point="org.eclipse.ui.handlers">
+      <handler
+            class="org.cauldron.sigil.handlers.project.ConvertProjectHandler"
+            commandId="org.cauldron.sigil.ui.convertproject">
+         <activeWhen>
+            <with
+                  variable="selection">
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+					<instanceof
+	                           value="org.eclipse.core.resources.IProject">
+	                </instanceof>
+		            <test
+						property="org.cauldron.sigil.isSigilProject"
+						value="false">
+					</test>
+               </iterate>
+            </with>
+         </activeWhen>
+      </handler>      
+      <handler
+            class="org.cauldron.sigil.handlers.project.RefreshSigilClasspathHandler"
+            commandId="org.cauldron.sigil.ui.refreshclasspath">
+         <activeWhen>
+            <with
+                  variable="selection">
+               <iterate
+                     ifEmpty="false"
+                     operator="and">
+					<instanceof
+	                           value="org.eclipse.core.resources.IProject">
+	                </instanceof>
+		            <test
+						property="org.cauldron.sigil.isSigilProject"
+						value="true">
+					</test>
+               </iterate>
+            </with>
+         </activeWhen>
+      </handler>      
+   </extension>   
+</plugin>
diff --git a/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
diff --git a/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
new file mode 100644
index 0000000..e0ba4a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.ui" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appinfo>
+         <meta.schema plugin="org.cauldron.sigil.ui" id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard"/>
+      </appinfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appinfo>
+            <meta.element />
+         </appinfo>
+      </annotation>
+      <complexType>
+         <sequence minOccurs="0" maxOccurs="unbounded">
+            <element ref="wizard"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute translatable="true"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="wizard">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appinfo>
+                  <meta.attribute kind="java" basedOn="org.cauldron.sigil.ui.wizard.repository.RepositoryWizard:"/>
+               </appinfo>
+            </annotation>
+         </attribute>
+         <attribute name="repository" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="since"/>
+      </appinfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="examples"/>
+      </appinfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="apiinfo"/>
+      </appinfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appinfo>
+         <meta.section type="implementation"/>
+      </appinfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
new file mode 100644
index 0000000..852f972
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public abstract class DisplayAction extends Action {
+
+	public DisplayAction() {
+		super();
+	}
+
+	public DisplayAction(String text) {
+		super(text);
+	}
+
+	public DisplayAction(String text, ImageDescriptor image) {
+		super(text, image);
+	}
+
+	public DisplayAction(String text, int style) {
+		super(text, style);
+	}
+
+	protected Display findDisplay() {
+		Display d = Display.getCurrent();
+		
+		if ( d == null ) {
+			d = Display.getDefault();
+		}
+		
+		return d;
+	}
+
+	protected void runInUI(final Shell shell, final WorkspaceModifyOperation op) {
+	}
+
+	protected void info(final Shell shell, final String msg) {
+		shell.getDisplay().asyncExec( new Runnable() {
+			public void run() {
+				MessageDialog.openInformation(shell, "Information", msg );
+			}
+		} );
+	}
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
new file mode 100644
index 0000000..86886bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class PruneProjectDependenciesAction extends DisplayAction {
+	
+	private ISigilProjectModel project;
+	
+	public PruneProjectDependenciesAction(ISigilProjectModel project) {
+		this.project = project;
+	}
+
+	@Override
+	public void run() {
+ 		final Shell shell = findDisplay().getActiveShell();
+ 		
+		Job job = new Job("Resolving imports") {
+
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				Collection<IModelElement> unused = JavaHelper.findUnusedReferences(project, monitor);
+				
+				if ( unused.isEmpty() ) {
+					info( shell, "No unused references found" );
+				}
+				else {
+					final ResourceReviewDialog<IModelElement> dialog = new ResourceReviewDialog<IModelElement>(shell, "Review Unused Imports", unused);
+					
+					shell.getDisplay().asyncExec( new Runnable() {
+						public void run() {
+							if ( dialog.open() == Window.OK ) {
+								WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+									@Override
+									protected void execute(IProgressMonitor monitor) throws CoreException {
+										for ( IModelElement e : dialog.getResources() ) {
+											if ( !project.getBundle().getBundleInfo().removeChild(e) ) {
+												SigilCore.error( "Failed to remove " + e );
+											}
+										}
+										
+										project.save(monitor);
+									}			
+								};
+								
+								SigilUI.runWorkspaceOperation(op, shell);
+							}
+						}
+					});
+				}
+				
+				return Status.OK_STATUS;
+			}
+		};
+		
+		job.schedule();
+		
+		IProgressService p = PlatformUI.getWorkbench().getProgressService();
+		p.showInDialog(shell, job);				
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
new file mode 100644
index 0000000..39d3abd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshRepositoryAction extends DisplayAction {
+	private final IRepositoryModel[] model;
+
+	public RefreshRepositoryAction(IRepositoryModel... model) {
+		super( "Refresh repository");
+		this.model = model;
+	}
+	
+	@Override
+	public void run() {
+		WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+
+			@Override
+			protected void execute(IProgressMonitor monitor)
+					throws CoreException, InvocationTargetException,
+					InterruptedException {
+				boolean changed = false;
+				
+				for ( IBundleRepository b : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+					for ( IRepositoryModel m : model ) {
+						if ( b.getId().equals( m.getId() ) ) {
+							b.refresh();
+							changed = true;
+						}
+					}
+				}
+				
+				if ( changed ) {
+					List<ISigilProjectModel> projects = SigilCore.getRoot().getProjects();
+					SubMonitor sub = SubMonitor.convert(monitor, projects.size() * 10);
+					for ( ISigilProjectModel p : projects ) {
+						p.resetClasspath(sub.newChild(10));
+					}
+				}
+			}
+		};
+		
+		SigilUI.runWorkspaceOperation(op, null);
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
new file mode 100644
index 0000000..1f1ec1e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class ResolveProjectDependenciesAction extends DisplayAction {
+
+	private ISigilProjectModel project;
+	private boolean review;
+
+	public ResolveProjectDependenciesAction(ISigilProjectModel project, boolean review) {
+		this.project = project;
+		this.review = review;
+	}
+	
+	public void run() {
+		final Shell shell = findDisplay().getActiveShell();
+		
+		Job job = new Job("Resolving dependencies" ) {
+			@Override
+			protected IStatus run(IProgressMonitor monitor) {
+				monitor.beginTask("", IProgressMonitor.UNKNOWN);
+				
+				List<IPackageImport> imports = JavaHelper.findRequiredImports(project, monitor);
+				
+				if ( imports.isEmpty() ) {
+					info( shell, "No new dependencies found" );
+				}
+				else {
+					Collections.sort(imports, new Comparator<IPackageImport>() {
+						public int compare(IPackageImport o1, IPackageImport o2) {
+							int i = o1.getPackageName().compareTo(o2.getPackageName());
+
+							// shouldn't get more than one import for same package
+							// but may as well sort if do...
+							if ( i == 0 ) {
+								i = o1.getVersions().getFloor().compareTo(o2.getVersions().getFloor() );
+							}
+							
+							return i;
+						}
+					});
+					
+					final ResourceReviewDialog<IPackageImport> dialog = new ResourceReviewDialog<IPackageImport>(shell, "Review New Dependencies", imports);					
+					shell.getDisplay().asyncExec( new Runnable() {
+						public void run() {
+							if ( !review || dialog.open() == Window.OK ) {
+								WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+									@Override
+									protected void execute(IProgressMonitor monitor) throws CoreException {
+										for ( IPackageImport pi : dialog.getResources() ) {
+											project.getBundle().getBundleInfo().addImport(pi);
+										}
+										
+										project.save(monitor);
+									}			
+								};
+								
+								SigilUI.runWorkspaceOperation(op, shell);
+							}
+						}
+					} );
+				}
+				
+				return Status.OK_STATUS;
+			}
+		};
+		
+		job.schedule();
+		
+		IProgressService p = PlatformUI.getWorkbench().getProgressService();
+		p.showInDialog(shell, job);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
new file mode 100644
index 0000000..69d8894
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ThreadProgressMonitor;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilClassPathContainer implements IClasspathContainer {
+
+    private IClasspathEntry[] entries;
+    private ISigilProjectModel sigil;
+
+    public SigilClassPathContainer(ISigilProjectModel sigil) {
+    	this.sigil = sigil;
+	}
+
+	/* (non-Javadoc)
+     * @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
+     */
+    public IClasspathEntry[] getClasspathEntries() {
+        if ( entries == null ) {
+        	buildClassPathEntries();
+        }
+        
+        return entries;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
+     */
+    public String getDescription() {
+        return "Bundle Context Classpath";
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jdt.core.IClasspathContainer#getKind()
+     */
+    public int getKind() {
+        return K_SYSTEM;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jdt.core.IClasspathContainer#getPath()
+     */
+    public IPath getPath() {
+        return new Path( SigilCore.CLASSPATH_CONTAINER_PATH );
+    }
+
+    /**
+     * @return
+     * @throws CoreException 
+     * @throws CoreException 
+     */
+    private void buildClassPathEntries() {
+		try {
+			IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+			entries = sigil.findExternalClasspath(monitor).toArray( new IClasspathEntry[0] );
+		} catch (CoreException e) {
+    		SigilCore.error( "Failed to build classpath entries", e);
+		}
+		finally {
+			if ( entries == null ) {
+	    		entries = new IClasspathEntry[] {};
+			}
+		}
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
new file mode 100644
index 0000000..f0229ee
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;

+

+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.job.*;
+import org.eclipse.core.runtime.CoreException;

+import org.eclipse.core.runtime.IPath;

+import org.eclipse.core.runtime.IProgressMonitor;

+import org.eclipse.core.runtime.jobs.Job;

+import org.eclipse.jdt.core.ClasspathContainerInitializer;

+import org.eclipse.jdt.core.IClasspathContainer;

+import org.eclipse.jdt.core.IJavaProject;

+import org.eclipse.jdt.core.JavaCore;

+

+public class SigilClasspathContainerInitializer extends ClasspathContainerInitializer {

+

+	public SigilClasspathContainerInitializer() {

+		// TODO Auto-generated constructor stub

+	}

+

+	@Override
+	public boolean canUpdateClasspathContainer(IPath containerPath,
+			IJavaProject project) {
+		return true;
+	}
+
+	@Override
+	public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion)
+			throws CoreException {
+		ISigilProjectModel sigil = SigilCore.create(project.getProject());
+		
+        IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
+		
+		IJavaProject[] affectedProjects = new IJavaProject[] { project };
+        
+        IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
+        
+        IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+        
+        if ( monitor == null ) { 
+        	monitor = Job.getJobManager().createProgressGroup();
+        }
+
+        JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);
+	}
+
+	@Override

+	public void initialize(IPath containerPath, IJavaProject project)

+			throws CoreException {
+		ISigilProjectModel sigil = SigilCore.create(project.getProject());
+		

+        IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);

+		

+		IJavaProject[] affectedProjects = new IJavaProject[] { project };

+        

+        IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };

+        

+        IProgressMonitor monitor = Job.getJobManager().createProgressGroup();

+

+        JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);

+	}

+

+}

diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
new file mode 100644
index 0000000..cd73c8f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.wizards.IClasspathContainerPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class SigilLibraryPage extends WizardPage implements IClasspathContainerPage {
+
+    private IClasspathEntry classpathEntry;
+
+    public SigilLibraryPage() {
+        super( "Sigil" );
+    }
+    
+    public void createControl(Composite parent) {
+        setTitle("Sigil Library");
+
+        Label label = new Label(parent, SWT.NONE);
+        label.setText("Press Finish to add the Sigil Library");
+        label.setFont(parent.getFont());
+
+        setControl(label);
+    }
+
+    public boolean finish() {
+        classpathEntry = JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH));
+        return true;
+    }
+
+    public boolean isPageComplete() {
+        return true;
+    }
+
+    public IClasspathEntry getSelection() {
+        return classpathEntry;
+    }
+
+    public void setSelection(IClasspathEntry containerEntry) {
+        this.classpathEntry = containerEntry;
+    }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
new file mode 100644
index 0000000..94184a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+
+public abstract class AbstractResourceCommandHandler extends AbstractHandler {
+
+	protected abstract IResourceCommandHandler getResourceCommandHandler();
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
new file mode 100644
index 0000000..8bbcc6f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class EditorResourceCommandHandler extends AbstractResourceCommandHandler {
+	
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+		final Shell shell = HandlerUtil.getActiveShell(event);
+		final IEditorPart editorPart = HandlerUtil.getActiveEditor(event);
+		if ( editorPart != null ) {
+			IEditorInput editorInput = editorPart.getEditorInput();
+			
+			if(!(editorInput instanceof IFileEditorInput)) {
+				throw new ExecutionException("Editor input must be a file");
+			}
+			IFileEditorInput fileInput = (IFileEditorInput) editorInput;
+			
+			try {
+				// Save the editor content (if dirty)
+				IRunnableWithProgress saveOperation = new IRunnableWithProgress() {
+					public void run(IProgressMonitor monitor) throws InvocationTargetException,
+					InterruptedException {
+						if(editorPart.isDirty()) {
+							if(MessageDialog.openQuestion(shell, "Save File", "The file contents must be saved before the command can be executed. Do you wish to save now?")) {
+								editorPart.doSave(monitor);
+							} else {
+								throw new InterruptedException();
+							}
+						}
+					}
+				};
+				new ProgressMonitorDialog(shell).run(false, true, saveOperation);
+	
+				// Execute on the file
+				IFile file = fileInput.getFile();
+				getResourceCommandHandler().execute(new IResource[] { file }, event);
+			} catch (InvocationTargetException e) {
+				throw new ExecutionException("Error saving file", e.getTargetException());
+			} catch (InterruptedException e) {
+				// Exit the command silently
+			}
+		}			
+		return null;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
new file mode 100644
index 0000000..a1eb206
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+
+public interface IResourceCommandHandler {
+	
+	Object execute(IResource[] resources, ExecutionEvent event) throws ExecutionException;
+	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
new file mode 100644
index 0000000..b96cc73
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class SelectionResourceCommandHandler extends AbstractResourceCommandHandler {
+	
+	public final Object execute(ExecutionEvent event) throws ExecutionException {
+		ISelection selection = HandlerUtil.getCurrentSelection(event);
+		
+		Object[] objectArray = ((IStructuredSelection) selection).toArray();
+		IResource[] resourceArray = new IResource[objectArray.length];
+		System.arraycopy(objectArray, 0, resourceArray, 0, objectArray.length);
+		
+		return getResourceCommandHandler().execute(resourceArray, event);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
new file mode 100644
index 0000000..8e93c4e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ConvertProjectCommandHandler implements IResourceCommandHandler {
+
+	public Object execute(IResource[] resources, ExecutionEvent event)
+			throws ExecutionException {
+		for ( IResource r : resources ) {
+			final IProject project = (IProject) r;
+			if ( project != null ) {
+				WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+					@Override
+					protected void execute(IProgressMonitor monitor)
+							throws CoreException {
+						SigilCore.makeSigilProject(project, monitor);
+						IJavaProject java = JavaCore.create(project);
+						ISigilProjectModel sigil = SigilCore.create(project);
+						IClasspathEntry[] entries = java.getRawClasspath();
+						for (int i = 0; i < entries.length; i++) {
+							IClasspathEntry entry = entries[i];
+							if(entry.getEntryKind()==IClasspathEntry.CPE_SOURCE) {
+								String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry( entry );
+								sigil.getBundle().addClasspathEntry(encodedClasspath);
+							}
+						}
+						sigil.save(monitor);
+					}				
+				};
+				SigilUI.runWorkspaceOperation(op, null);
+			}
+		}
+		
+		return null;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
new file mode 100644
index 0000000..bf012f8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class ConvertProjectHandler extends SelectionResourceCommandHandler {
+
+	@Override
+	protected IResourceCommandHandler getResourceCommandHandler() {
+		return new ConvertProjectCommandHandler();
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
new file mode 100644
index 0000000..0df3770
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshSigilClasspathCommandHandler implements
+		IResourceCommandHandler {
+
+	public Object execute(IResource[] resources, ExecutionEvent event)
+			throws ExecutionException {
+		try {
+			for ( IResource res : resources ) {
+				IProject p = (IProject) res;
+				final ISigilProjectModel model = SigilCore.create(p);
+				
+				WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+					@Override
+					protected void execute(IProgressMonitor monitor)
+							throws CoreException, InvocationTargetException,
+							InterruptedException {
+						model.resetClasspath(monitor);
+					}
+				};
+				
+				SigilUI.runWorkspaceOperation(op, null);
+			}
+		} catch (CoreException e) {
+			SigilCore.error( "Failed to create sigil project for refresh action", e );
+		}
+		return null;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
new file mode 100644
index 0000000..6dc20bb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class RefreshSigilClasspathHandler extends SelectionResourceCommandHandler {
+
+	@Override
+	protected IResourceCommandHandler getResourceCommandHandler() {
+		return new RefreshSigilClasspathCommandHandler();
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
new file mode 100644
index 0000000..3f437bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.startup;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ResolveProjectsJob;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.ui.IStartup;
+
+public class SigilStartup implements IStartup {
+
+	public void earlyStartup() {
+		// Create a task to run the resolver
+		final Runnable resolver = new Runnable() {
+			public void run() {
+				IWorkspace workspace = ResourcesPlugin.getWorkspace();
+				ResolveProjectsJob job = new ResolveProjectsJob(workspace);
+				job.setSystem(true);
+				job.schedule();
+			}
+		};
+		
+		// Register a repository change listener to re-run the resolver when repository changes
+		SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(new IRepositoryChangeListener() {
+			public void repositoryChanged(RepositoryChangeEvent event) {
+				resolver.run();
+			}
+		});
+
+		// Run the resolver now
+		resolver.run();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
new file mode 100644
index 0000000..a3c70d5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilUI extends AbstractUIPlugin {
+
+	// The plug-in ID
+	public static final String PLUGIN_ID = "org.cauldron.sigil.ui";
+
+	public static final String REPOSITORY_WIZARD_EXTENSION_POINT_ID = "org.cauldron.sigil.ui.repositorywizard";
+
+	public static final String PREF_NOPROMPT_INSTALL_COMPOSITE_WITH_ERRORS = "nopromptInstallCompositeError";
+	public static final String PREF_INSTALL_COMPOSITE_WITH_ERRORS_ANSWER = "answerInstallCompositeError";
+
+	public static final String ID_REPOSITORY_VIEW = "org.cauldron.sigil.ui.repositoryBrowser";
+	public static final String ID_DEPENDENCY_VIEW = "org.cauldron.sigil.ui.bundleDependencyView";
+	public static final String ID_INSTANCES_VIEW = "org.cauldron.sigil.runtime.newtonInstancesView";
+
+	// The shared instance
+	private static SigilUI plugin;
+	
+	/**
+	 * The constructor
+	 */
+	public SigilUI() {
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+	 */
+	public void start(BundleContext context) throws Exception {
+		super.start(context);
+		SigilCore.getDefault();
+		plugin = this;
+	}
+
+	/*
+	 * (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);
+	}
+
+	/**
+	 * Returns the shared instance
+	 *
+	 * @return the shared instance
+	 */
+	public static SigilUI getDefault() {
+		return plugin;
+	}
+
+	public static ResourceBundle getResourceBundle() {
+		return ResourceBundle.getBundle( "resources." + SigilUI.class.getName(), Locale.getDefault(), SigilUI.class.getClassLoader() );
+	}
+
+	public static void runWorkspaceOperation(IRunnableWithProgress op, Shell shell) {
+		if ( shell == null ) {
+			shell = getActiveWorkbenchShell();
+		}
+		try {
+			new ProgressMonitorDialog(shell).run(true, true, op);
+		} catch (InvocationTargetException e) {
+			SigilCore.error( "Workspace operation failed", e);
+		} catch (InterruptedException e1) {
+			SigilCore.log( "Workspace operation interrupted");
+		}
+	}
+	
+	public static void runWorkspaceOperationSync(IRunnableWithProgress op, Shell shell) throws Throwable {
+		if ( shell == null ) {
+			shell = getActiveWorkbenchShell();
+		}
+		try {
+			new ProgressMonitorDialog(shell).run(false, true, op);
+		} catch (InvocationTargetException e) {
+			throw e.getCause();
+		} catch (InterruptedException e1) {
+			SigilCore.log( "Workspace operation interrupted");
+		}
+	}
+	public static IWorkbenchWindow getActiveWorkbenchWindow() {
+		return getDefault().getWorkbench().getActiveWorkbenchWindow();
+	}
+	
+	public static Shell getActiveWorkbenchShell() {
+		final Shell[] shell = new Shell[1];
+		runInUISync( new Runnable() {
+			public void run() {
+				shell[0] = getActiveDisplay().getActiveShell();
+			}
+		});
+		return shell[0];
+	}
+
+	public static Display getActiveDisplay() {
+		Display d = Display.getCurrent();
+		
+		if ( d == null ) {
+			d = Display.getDefault();
+		}
+
+		return d;
+	}
+
+	public static void runInUI(Runnable runnable) {
+		getActiveDisplay().asyncExec(runnable);
+	}
+
+	public static void runInUISync(Runnable runnable) {
+		getActiveDisplay().syncExec(runnable);
+	}
+	
+	public static Image cacheImage(String path, ClassLoader classLoader) {
+		ImageRegistry registry = SigilUI.getDefault().getImageRegistry();
+		
+		Image image = registry.get( path );
+		
+		if ( image == null ) {
+			image = loadImage( path, classLoader );
+			// XXX-FIXME-XXX add null image
+			if ( image != null ) {
+				registry.put( path, image);
+			}
+		}
+		
+		return image; 
+	}
+
+	private static Image loadImage(String resource, ClassLoader loader) {
+		InputStream in = loader.getResourceAsStream( resource );
+		if ( in != null ) {
+			ImageData data = new ImageData( in );
+			return new Image( SigilUI.getActiveDisplay(), data );
+		}
+		else {
+			return null;
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
new file mode 100644
index 0000000..b8e7d2e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.completion;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+class CompletionProposal implements ICompletionProposal {
+
+	private int fReplacementOffset;
+	private int fReplacementLength;
+	private int fCursorPosition;
+	private String fReplacementString;
+	private String fAdditionalProposalInfo;
+	private IContextInformation fContextInformation;
+	private String fDisplayString;
+	private Image fImage;
+	
+	/**
+	 * Creates a new completion proposal based on the provided information. The replacement string is
+	 * considered being the display string too. All remaining fields are set to <code>null</code>.
+	 *
+	 * @param replacementString the actual string to be inserted into the document
+	 * @param replacementOffset the offset of the text to be replaced
+	 * @param replacementLength the length of the text to be replaced
+	 * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+	 */
+	public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition) {
+		this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
+	}
+
+	/**
+	 * Creates a new completion proposal. All fields are initialized based on the provided information.
+	 *
+	 * @param replacementString the actual string to be inserted into the document
+	 * @param replacementOffset the offset of the text to be replaced
+	 * @param replacementLength the length of the text to be replaced
+	 * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+	 * @param image the image to display for this proposal
+	 * @param displayString the string to be displayed for the proposal
+	 * @param contextInformation the context information associated with this proposal
+	 * @param additionalProposalInfo the additional information associated with this proposal
+	 */
+	public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+		fReplacementString= replacementString;
+		fReplacementOffset= replacementOffset;
+		fReplacementLength= replacementLength;
+		fCursorPosition= cursorPosition;
+		fImage= image;
+		fDisplayString= displayString;
+		fContextInformation= contextInformation;
+		fAdditionalProposalInfo= additionalProposalInfo;
+	}
+	
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+	 */
+	public void apply( IDocument document ) {
+		try {
+			document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
+		} catch (BadLocationException x) {
+			// ignore
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+	 */
+	public String getAdditionalProposalInfo() {
+		return fAdditionalProposalInfo;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+	 */
+	public IContextInformation getContextInformation() {
+		return fContextInformation;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+	 */
+	public String getDisplayString() {
+		if (fDisplayString != null)
+			return fDisplayString;
+		return fReplacementString;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+	 */
+	public Image getImage() {
+		return fImage;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+	 */
+	public Point getSelection( IDocument document ) {
+		return new Point(fReplacementOffset + fCursorPosition, 0);
+	}
+	
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
new file mode 100644
index 0000000..2ba92ca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.File;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.forms.IManagedForm;
+
+public abstract class AbstractResourceSection extends SigilSection implements IResourceChangeListener, ICheckStateListener  {
+
+	protected static final Object[] EMPTY = new Object[]{};
+	protected Tree tree;
+	protected CheckboxTreeViewer viewer;
+	private IWorkspace workspace;
+
+	public AbstractResourceSection(SigilPage page, Composite parent,
+			ISigilProjectModel project) throws CoreException {
+		super(page, parent, project);
+	}
+
+	@Override
+	public void initialize(IManagedForm form) {
+		super.initialize(form);
+		viewer.setAllChecked(false);
+		viewer.setGrayedElements( EMPTY );
+		refreshSelections();
+		viewer.refresh();		
+	}
+	
+	@Override
+	public void refresh() {
+		refreshSelections();
+		super.refresh();
+	}
+
+	public void checkStateChanged(CheckStateChangedEvent event) {
+		handleStateChanged( (IResource) event.getElement(), event.getChecked(), true, true );
+	}
+
+	public void resourceChanged(IResourceChangeEvent event) {
+		if ( !getSection().getDisplay().isDisposed() ) {
+			getSection().getDisplay().asyncExec( new Runnable() {
+				public void run() {
+					if ( getSection().isVisible() ) {
+						viewer.refresh();
+					}
+				}			
+			});
+		}
+	}
+	
+	protected void startWorkspaceListener(IWorkspace workspace) {
+		this.workspace = workspace;
+		workspace.addResourceChangeListener(this);
+	}
+	
+	@Override
+	public void dispose() {
+		workspace.removeResourceChangeListener(this);
+	}
+
+	protected abstract void refreshSelections();
+	
+	protected abstract void syncResourceModel(IResource element, boolean checked);
+
+	protected IResource findResource(IPath path) {
+		IProject project2 = getProjectModel().getProject();
+		File f = project2.getLocation().append(path).toFile();
+		
+		if ( f.exists() ) {
+			try {
+				if ( f.isFile() ) {
+					return project2.getFile(path);
+				}
+				else {
+					return project2.getFolder(path);
+				}
+			}
+			catch (IllegalArgumentException e) {
+				SigilCore.error( "Unknown path " + path );
+				return null;
+			}
+		}
+		else {
+			SigilCore.error( "Unknown file " + f );
+			return null;
+		}
+	}	
+	
+
+	protected void handleStateChanged(IResource element, boolean checked, boolean recurse,
+			boolean sync) {
+				if ( element instanceof IContainer ) {
+					setParentsGrayChecked(element, checked);
+					if ( recurse ) {
+						recursiveCheck( (IContainer) element, checked, sync );
+					}
+				}
+				else {
+					setParentsGrayChecked(element, checked);
+				}
+				
+				if ( sync ) {
+					syncResourceModel( element, checked );
+				}
+			}
+
+	private void recursiveCheck(IContainer element, final boolean checked, final boolean sync) {
+		try {
+			element.accept( new IResourceVisitor() {
+				public boolean visit(IResource resource) throws CoreException {
+					viewer.setChecked(resource, checked);
+					if ( sync ) {
+						syncResourceModel(resource, checked);
+					}
+					return true;
+				}
+			} );
+		}
+		catch ( CoreException e ) {
+			DebugPlugin.log( e.getStatus() );
+		}
+	}
+
+	private void setParentsGrayChecked(IResource r, boolean checked) {
+		while ( r.getParent() != null ) {
+			r = r.getParent();
+			if ( (viewer.getGrayed(r) && viewer.getChecked(r)) == checked) {
+				break;
+			}
+			if ( viewer.getChecked(r) == checked ) {
+				viewer.setGrayed( r, !checked );
+			}
+			else {
+				viewer.setGrayChecked(r, checked);
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
new file mode 100644
index 0000000..0750e0f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public abstract class BundleDependencySection extends SigilSection {
+
+	private Set<? extends IModelElement> unresolvedElements;
+	
+	protected ProjectTableViewer viewer;
+	private Button add;
+	private Button edit;
+	private Button remove;
+
+	public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+		super(page, parent, project);
+		viewer.setUnresolvedElements(unresolvedElements);
+	}
+	
+	public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		this(page, parent, project, null);
+	}
+	
+	protected abstract String getTitle();
+	
+	protected abstract Label createLabel(Composite parent, FormToolkit toolkit);
+	
+	protected abstract IContentProvider getContentProvider();
+
+	protected abstract void handleAdd();
+	
+	protected abstract void handleEdit();
+	
+	protected abstract void handleRemoved();
+	
+	@Override
+	public void refresh() {
+		super.refresh();
+		viewer.refresh();
+	}
+	
+	protected ISelection getSelection() {
+		return viewer.getSelection();
+	}
+
+	@Override
+	protected void createSection(Section section, FormToolkit toolkit) {
+		setTitle( getTitle() );
+		
+		Composite body = createGridBody(2, false, toolkit);
+		
+        Label label = createLabel(body, toolkit);
+        
+        label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+        
+		Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+		GridData data = new GridData( GridData.FILL_BOTH );
+		data.heightHint = 600;
+        bundleTable.setLayoutData( data );
+        bundleTable.setLinesVisible(false);        
+        createButtons(body, toolkit);        
+        createViewer( bundleTable );
+	}
+	
+	protected void createButtons(Composite body, FormToolkit toolkit) {
+	    Composite buttons = toolkit.createComposite(body);
+	    TableWrapLayout layout = new TableWrapLayout();
+	    layout.numColumns = 1;
+	    layout.topMargin = 0;
+	    layout.leftMargin = 0;
+	    layout.rightMargin = 0;
+	    layout.bottomMargin = 0;
+	    buttons.setLayout( layout );
+	    GridData data = new GridData();
+	    data.verticalAlignment = SWT.TOP;
+	    buttons.setLayoutData(data);
+	    
+	    add = toolkit.createButton(buttons, "Add", SWT.NULL);
+	    add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+	    add.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleAdd();
+			}        	
+	    } );
+	    
+	    edit = toolkit.createButton(buttons, "Edit", SWT.NULL);
+	    edit.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+	    edit.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleEdit();
+			}        	
+	    } );
+	    
+	    remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+	    remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+	    remove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleRemoved();
+			}
+	    } );
+	    
+	    setSelected(false);
+	}
+
+	protected void createViewer(Table bundleTable) {
+	    viewer = new ProjectTableViewer(bundleTable);
+	    viewer.setContentProvider(getContentProvider());  
+	    viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				setSelected(!event.getSelection().isEmpty());
+			}
+	    });
+	}
+
+	private void setSelected(boolean selected) {
+		edit.setEnabled(selected);
+		remove.setEnabled(selected);
+	}
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
new file mode 100644
index 0000000..311acca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.IFilter;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ClasspathSection extends SigilSection {
+
+	private ProjectTableViewer viewer;
+	private final Comparator<IClasspathEntry> CLASSPATH_COMPARATOR = new Comparator<IClasspathEntry>() {
+		public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+			return compareClasspaths(o1, o2);
+		}
+	};
+
+	public ClasspathSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}
+	
+	@Override
+	protected void createSection(Section section, FormToolkit toolkit) {
+		setTitle( "Classpath");
+		
+		Composite body = createGridBody(2, false, toolkit);
+		
+        Label label = toolkit.createLabel( body, "Specify the internal classpath of this bundle." );
+        label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+		
+		Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+		GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+		tableLayoutData.heightHint = 150;
+        bundleTable.setLayoutData(tableLayoutData);
+        
+        createButtons(body, toolkit);
+        createViewer( bundleTable );
+	}
+
+	private void createButtons(Composite body, FormToolkit toolkit) {
+        Composite buttons = toolkit.createComposite(body);
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.numColumns = 1;
+        layout.topMargin = 0;
+        layout.leftMargin = 0;
+        layout.rightMargin = 0;
+        layout.bottomMargin = 0;
+        buttons.setLayout( layout );
+        
+        Button add = toolkit.createButton(buttons, "Add", SWT.NULL);
+        add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+        add.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleAdd();
+			}        	
+        } );
+        
+        Button remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+        remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+        remove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleRemoved();
+			}
+        } );
+	}
+
+	private void createViewer(Table bundleTable) {
+        viewer = new ProjectTableViewer(bundleTable);
+        viewer.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				ArrayList<IClasspathEntry> cp = new ArrayList<IClasspathEntry>();
+				for (IClasspathEntry cpe : JavaHelper.findClasspathEntries(getBundle())) {
+					cp.add(cpe);
+				}
+				
+				Collections.sort(cp, new Comparator<IClasspathEntry>() {
+					public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+						return o1.toString().compareTo(o2.toString());
+					}
+				});
+				return cp.toArray();
+			}
+        });    
+        viewer.setComparator(new ViewerComparator() {
+			@Override
+			public int category(Object element) {
+				return index((IClasspathEntry) element);
+			}
+        });
+	}
+	
+	protected ISigilBundle getBundle() {
+		return getProjectModel().getBundle();
+	}
+
+	private void handleAdd() {
+		try {
+			BackgroundLoadingSelectionDialog<IClasspathEntry> dialog = new BackgroundLoadingSelectionDialog<IClasspathEntry>(getSection().getShell(), "Classpath Entry:", true);
+			
+			dialog.setDescriptor(new IElementDescriptor<IClasspathEntry>() {
+				public String getName(IClasspathEntry element) {
+					return element.getPath().toString();
+				}
+			
+				public String getLabel(IClasspathEntry element) {
+					return getName(element);
+				}
+			});
+			
+			dialog.setLabelProvider(new ModelLabelProvider());
+			
+			dialog.setFilter(new IFilter<IClasspathEntry>() {
+				public boolean select(IClasspathEntry cp) {
+					switch ( cp.getEntryKind() ) {
+					case IClasspathEntry.CPE_LIBRARY:
+					case IClasspathEntry.CPE_VARIABLE:
+					case IClasspathEntry.CPE_SOURCE:
+						return !getBundle().getClasspathEntrys().contains(encode(cp));
+					default:
+						return false;
+					}
+				}
+			});
+			
+			dialog.setComparator(CLASSPATH_COMPARATOR);
+			
+			IClasspathEntry[] classpath = getProjectModel().getJavaModel().getRawClasspath();
+			dialog.addElements(Arrays.asList(classpath));
+			if ( dialog.open() == Window.OK ) {
+				List<IClasspathEntry> selectedElements = dialog.getSelectedElements();
+				
+				Object[] added = selectedElements.toArray();
+				for (IClasspathEntry entry : selectedElements) {
+					getBundle().addClasspathEntry(encode(entry));
+				}
+				viewer.add(added);
+				viewer.refresh();
+				markDirty();
+			}
+		} catch (JavaModelException e) {
+			ErrorDialog.openError(getSection().getShell(), "Error", null, e.getStatus());
+		}
+	}
+
+	private int compareClasspaths(IClasspathEntry o1, IClasspathEntry o2) {
+		if ( o1.getEntryKind() == o2.getEntryKind() ) {
+			ModelLabelProvider mlp = viewer.getLabelProvider();
+			return mlp.getText(o1).compareTo(mlp.getText(o2));
+		}
+		else {
+			int i1 = index(o1);
+			int i2 = index(o2);
+			
+			if ( i1 < i2 ) {
+				return -1;
+			}
+			else {
+				return 1;
+			}
+		}
+	}
+
+	private static int index(IClasspathEntry o1) {
+		switch ( o1.getEntryKind() ) {
+		case IClasspathEntry.CPE_SOURCE: return 0;
+		case IClasspathEntry.CPE_PROJECT: return 1;
+		case IClasspathEntry.CPE_LIBRARY: return 2;
+		case IClasspathEntry.CPE_VARIABLE: return 3;
+		case IClasspathEntry.CPE_CONTAINER: return 4;
+		default: throw new IllegalStateException( "Unknown classpath entry type " + o1);
+		}
+	}
+
+	private String encode(IClasspathEntry cp) {
+		return getProjectModel().getJavaModel().encodeClasspathEntry(cp).trim();
+	}
+
+	@SuppressWarnings("unchecked")
+	private void handleRemoved() {
+		IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IClasspathEntry> i = selection.iterator(); i.hasNext(); ) {			
+				getBundle().removeClasspathEntry(getProjectModel().getJavaModel().encodeClasspathEntry(i.next()).trim());
+			}		
+			viewer.remove(selection.toArray());
+			markDirty();
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
new file mode 100644
index 0000000..5d091fb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+
+public class ContainerTreeProvider extends DefaultTreeContentProvider {
+
+	private static final Object[] EMPTY = new Object[] {};
+	
+	public Object[] getChildren(Object parentElement) {
+		if ( parentElement instanceof IContainer ) {
+			IContainer f = (IContainer) parentElement;
+			try {
+				return f.members();
+			} catch (CoreException e) {
+				DebugPlugin.log( e.getStatus() );
+			}
+		}
+		return EMPTY;	
+	}
+
+	public Object getParent(Object element) {
+		IResource r = (IResource) element;
+		return r.getParent();
+	}
+
+	public boolean hasChildren(Object element) {
+		if ( element instanceof IContainer ) {
+			return true;
+		}
+		else {
+			return false;
+		}
+	}
+
+	public Object[] getElements(Object inputElement) {
+		IContainer container = (IContainer) inputElement;
+		return getChildren(container);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
new file mode 100644
index 0000000..7d1fbcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ContentSummarySection extends SigilSection {
+
+	public ContentSummarySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}
+
+	@Override
+	protected void createSection(Section section, FormToolkit toolkit) {
+		setTitle( "Project Content");
+		
+		Composite body = createTableWrapBody(2, toolkit);
+		Hyperlink link = toolkit.createHyperlink( body, "Contents:", SWT.NONE );
+		link.setHref( ContentsForm.PAGE_ID );
+		link.addHyperlinkListener(this);
+		toolkit.createLabel( body, "Manage the content that this bundle provides." );
+		
+		link = toolkit.createHyperlink( body, "Dependencies:", SWT.NONE );
+		link.setHref( DependenciesForm.PAGE_ID );
+		link.addHyperlinkListener(this);
+		toolkit.createLabel( body, "Manage the dependencies that this bundle needs to run." );
+		
+		link = toolkit.createHyperlink( body, "Exports:", SWT.NONE );
+		link.setHref( ExportsForm.PAGE_ID );
+		link.addHyperlinkListener(this);
+		toolkit.createLabel( body, "Manage the resources that this bundle exports." );
+	}
+
+	@Override
+	public void linkActivated(HyperlinkEvent e) {
+		getPage().getEditor().setActivePage( (String) e.getHref() );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
new file mode 100644
index 0000000..62d58a8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ContentsForm extends SigilPage  {
+
+	public static final String PAGE_ID = "contents";
+	
+	private ISigilProjectModel project;
+	
+	public ContentsForm(FormEditor editor, ISigilProjectModel project) {
+		super(editor, PAGE_ID, "Contents");
+		this.project = project;
+	}
+	
+    @Override
+    protected void createFormContent(IManagedForm managedForm) {
+        FormToolkit toolkit = managedForm.getToolkit();
+        
+        ScrolledForm form = managedForm.getForm();
+        form.setText( "Contents" );
+        
+        Composite body = form.getBody();        
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.bottomMargin = 10;
+        layout.topMargin = 5;
+        layout.leftMargin = 10;
+        layout.rightMargin = 10;
+        layout.numColumns = 2;
+        layout.horizontalSpacing = 10;
+        body.setLayout(layout);
+        body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+        
+        Composite top = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        top.setLayout(layout);
+        TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+        data.colspan = 2;
+        top.setLayoutData(data);
+        
+        Composite left = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        left.setLayout(layout);
+        left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+        
+        Composite right = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        right.setLayout(layout);
+        right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+                
+        try {
+	        ClasspathSection classpath = new ClasspathSection( this, top, project );
+	        managedForm.addPart( classpath );
+	        
+	        ResourceBuildSection runtimeBuild = new ResourceBuildSection( this, left, project );
+	        managedForm.addPart( runtimeBuild );
+	        
+	        DownloadSection download = new DownloadSection( this, right, project );
+	        managedForm.addPart( download );	        
+        }
+        catch (CoreException e) {
+        	SigilCore.error( "Failed to create contents form", e);
+        }
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
new file mode 100644
index 0000000..1c28ba5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class DependenciesForm extends SigilPage  {
+
+	public static final String PAGE_ID = "dependencies";
+	
+	private final ISigilProjectModel project;
+	private final Set<IModelElement> unresolvedElements;
+
+	private ImportPackagesSection importPackages;
+	private RequiresBundleSection requiresSection;
+
+	
+	public DependenciesForm(FormEditor editor, ISigilProjectModel project, Set<IModelElement> unresolved) {
+		super(editor, PAGE_ID, "Dependencies");
+		this.project = project;
+		this.unresolvedElements = unresolved;
+	}
+	
+    @Override
+	public boolean canLeaveThePage() {
+    	return !isDirty();
+	}
+
+	@Override
+    protected void createFormContent(IManagedForm managedForm) {
+        FormToolkit toolkit = managedForm.getToolkit();
+        
+        ScrolledForm form = managedForm.getForm();
+        form.setText( "Dependencies" );
+        
+        Composite body = form.getBody();        
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.bottomMargin = 10;
+        layout.topMargin = 5;
+        layout.leftMargin = 10;
+        layout.rightMargin = 10;
+        layout.numColumns = 2;
+        layout.horizontalSpacing = 10;
+        body.setLayout(layout);
+        body.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+        
+        Composite left = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        left.setLayout(layout);
+        TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB);
+        layoutData.rowspan = 2;
+        left.setLayoutData(layoutData);
+        
+        Composite right = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        right.setLayout(layout);
+        right.setLayoutData(new TableWrapData( TableWrapData.FILL_GRAB) );
+        
+        Composite bottom = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        bottom.setLayout(layout);
+        bottom.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+                
+        try {
+	        importPackages = new ImportPackagesSection( this, left, project, unresolvedElements );
+	        managedForm.addPart( importPackages );
+	        
+	        requiresSection = new RequiresBundleSection(this, right, project, unresolvedElements );
+	        managedForm.addPart(requiresSection);
+	        
+	        DependencyManagementSection depMgmt = new DependencyManagementSection(this, bottom, project);
+	        managedForm.addPart(depMgmt);
+        }
+        catch (CoreException e) {
+        	SigilCore.error( "Failed to create dependencies form", e);
+        }
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
new file mode 100644
index 0000000..aadf990
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.AccumulatorAdapter;
+import org.cauldron.sigil.ui.util.ExportedPackageFinder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IImportDeclaration;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class DependencyManagementSection extends SigilSection {
+
+	private Hyperlink hypConvertRBtoIP;
+
+	public DependencyManagementSection(SigilPage page, Composite parent,
+			ISigilProjectModel project) throws CoreException {
+		super(page, parent, project);
+	}
+
+	protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+		setTitle("Dependency Management");
+		
+		Composite body = createGridBody(1, false, toolkit);
+		
+		hypConvertRBtoIP = toolkit.createHyperlink(body, "Convert Required Bundles to Imported Packages", SWT.NONE);
+		hypConvertRBtoIP.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		
+		hypConvertRBtoIP.addHyperlinkListener(new HyperlinkAdapter() {
+			public void linkActivated(HyperlinkEvent e) {
+				run();
+			}
+		});
+	}
+	
+	protected void run() {
+		final Map<String, IPackageExport> exports = new HashMap<String, IPackageExport>();
+		final Set<String> imports = new HashSet<String>();
+		
+		// Find all exports
+		final ExportedPackageFinder exportFinder = new ExportedPackageFinder(getProjectModel(), new AccumulatorAdapter<IPackageExport>() {
+			public void addElements(Collection<? extends IPackageExport> elements) {
+				for (IPackageExport export : elements) {
+					exports.put(export.getPackageName(), export);
+				}
+			}
+		});
+		Job findExportsJob = new Job("Find exports") {
+			protected IStatus run(IProgressMonitor monitor) {
+				return exportFinder.run(monitor);
+			}
+		};
+		findExportsJob.setUser(true);
+		findExportsJob.schedule();
+		
+		// Find imports from Java source
+		Job findImportsJob = new Job("Find imports") {
+			protected IStatus run(IProgressMonitor monitor) {
+				IJavaProject javaProject = getProjectModel().getJavaModel();
+				try {
+					IPackageFragment[] packages = javaProject.getPackageFragments();
+					for (IPackageFragment pkg : packages) {
+						ICompilationUnit[] compilationUnits = pkg.getCompilationUnits();
+						for (ICompilationUnit compilationUnit : compilationUnits) {
+							IImportDeclaration[] importDecls = compilationUnit.getImports();
+							for (IImportDeclaration importDecl : importDecls) {
+								imports.add(getPackageName(importDecl));
+							}
+						}
+					}
+					return Status.OK_STATUS;
+				} catch (JavaModelException e) {
+					return new Status(IStatus.ERROR, SigilUI.PLUGIN_ID, 0, "Error finding imports", e);
+				}
+			}
+		};
+		findImportsJob.setUser(true);
+		findImportsJob.schedule();
+		
+		// Wait for both jobs to complete
+		try {
+			findImportsJob.join();
+			findExportsJob.join();
+		} catch (InterruptedException e) {
+			// Aborted, just do nothing
+			return;
+		}
+		
+		// Get the version rules
+		IPreferenceStore prefStore = SigilCore.getDefault().getPreferenceStore();
+		VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+		VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+		
+		// Get the existing imports for the bundle
+		IBundleModelElement bundleInfo = getProjectModel().getBundle().getBundleInfo();
+		Set<IPackageImport> existingImports = bundleInfo.getImports();
+		Map<String, IPackageImport> existingImportsMap = new HashMap<String, IPackageImport>();
+		for (IPackageImport existingImport : existingImports) {
+			existingImportsMap.put(existingImport.getPackageName(), existingImport);
+		}
+		
+		// Add imports to the bundle
+		ModelElementFactory elementFactory = ModelElementFactory.getInstance();
+		int count = 0;
+		for (String pkgImport : imports) {
+			IPackageExport export = exports.get(pkgImport);
+			if(export != null && !existingImportsMap.containsKey(pkgImport)) {
+				VersionRange versionRange = VersionRange.newInstance(export.getVersion(), lowerBoundRule, upperBoundRule);
+				IPackageImport newImport = elementFactory.newModelElement(IPackageImport.class);
+				newImport.setPackageName(pkgImport);
+				newImport.setVersions(versionRange);
+				newImport.setOptional(false);
+				
+				bundleInfo.addImport(newImport);
+				count++;
+			}
+		}
+		
+		// Remove required bundles
+		Set<IRequiredBundle> requiredBundles = bundleInfo.getRequiredBundles();
+		int requiredBundlesSize = requiredBundles.size();
+		for (IRequiredBundle requiredBundle : requiredBundles) {
+			bundleInfo.removeRequiredBundle(requiredBundle);
+		}
+
+		// Update the editor
+		if(count + requiredBundlesSize > 0) {
+			IFormPart[] parts = getPage().getManagedForm().getParts();
+			for (IFormPart formPart : parts) {
+				formPart.refresh();
+				((AbstractFormPart) formPart).markDirty();
+			}
+		}
+		
+		MessageDialog.openInformation(getManagedForm().getForm().getShell(), "Dependency Management", "Removed " + requiredBundlesSize + " required bundle(s) and added " + count + " imported package(s).");
+	}
+	
+	private static String getPackageName(IImportDeclaration decl) {
+		String name = decl.getElementName();
+		int lastDot = name.lastIndexOf('.');
+		return name.substring(0, lastDot);
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
new file mode 100644
index 0000000..ad2e705
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class DownloadSection extends AbstractResourceSection {
+
+	/**
+	 * @param page
+	 * @param parent
+	 * @param project
+	 * @throws CoreException 
+	 */
+	
+	private IDownloadJar dl;
+	
+	public DownloadSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+	 */
+	@Override
+	protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+		setTitle( "Codebase" );
+		
+		Composite body = createTableWrapBody(1, toolkit);
+
+        toolkit.createLabel( body, "Specify which resources are included as part of this bundles remote codebase." );
+		
+		tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+		
+		TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+		data.heightHint = 200;
+		tree.setLayoutData( data );
+		
+		viewer = new CheckboxTreeViewer( tree );
+		IFolder base = ResourcesPlugin.getWorkspace().getRoot().getFolder(getProjectModel().getJavaModel().getOutputLocation());
+		viewer.setContentProvider( new ContainerTreeProvider() );
+		viewer.setLabelProvider( new ModelLabelProvider() );
+		viewer.addCheckStateListener( this );
+		viewer.setInput( base );
+		
+		dl = getProjectModel().getBundle().getDownloadJar();
+		
+		startWorkspaceListener(base.getWorkspace());
+	}
+
+	@Override
+	public void refresh() {
+		dl = getProjectModel().getBundle().getDownloadJar();
+		super.refresh();
+	}
+
+	@Override
+	public void commit(boolean onSave) {
+		getProjectModel().getBundle().setDownloadJar(dl);			
+		super.commit(onSave);
+	}
+
+	@Override
+	protected void refreshSelections() {
+		// zero the state
+		if ( dl != null ) {
+			for ( IPath path : dl.getEntrys() ) {
+				IResource r = findResource( path );
+				if ( r != null ) {
+					viewer.expandToLevel(r, 0);
+					viewer.setChecked( r, true );
+					viewer.setGrayed( r, false );
+					handleStateChanged(r, true, false, false);
+				}
+				else {
+					SigilCore.error( "Unknown path " + path );
+				}
+			}
+		}
+	}	
+	
+	protected void syncResourceModel(IResource element, boolean checked) {
+		try {
+			if ( dl == null ) {
+				dl = ModelElementFactory.getInstance().newModelElement(IDownloadJar.class);
+				getProjectModel().getBundle().setDownloadJar(dl);
+			}
+			
+			if ( checked ) {
+				dl.addEntry( element.getProjectRelativePath() );
+			}
+			else {
+				dl.removeEntry( element.getProjectRelativePath() );
+			}
+			
+			markDirty();
+		}
+		catch (ModelElementFactoryException e) {
+			SigilCore.error( "Failed to create model element", e );
+		}
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
new file mode 100644
index 0000000..4f91445
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class ExcludedResourcesFilter extends ViewerFilter {
+
+	private final Set<Pattern> exclusionSet = new HashSet<Pattern>();
+	
+	public ExcludedResourcesFilter() {
+		loadExclusions();
+	}
+
+	public final synchronized void loadExclusions() {
+		exclusionSet.clear();
+		IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+		String[] exclusions = PrefsUtils.stringToArray(store.getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES));
+		for (String exclusion : exclusions) {
+			exclusionSet.add(GlobCompiler.compile(exclusion));
+		}
+	}
+
+	@Override
+	public synchronized boolean select(Viewer viewer, Object parentElement, Object element) {
+		IResource file = (IResource) element;
+		String path = file.getName();
+		for ( Pattern p :exclusionSet ) {
+			if ( p.matcher(path).matches() ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
new file mode 100644
index 0000000..406a5e5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.osgi.framework.Version;
+
+public class ExportPackagesSection extends BundleDependencySection {
+
+	public ExportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}
+		
+	@Override
+	protected String getTitle() {
+		return "Export Packages";
+	}
+
+	@Override
+	protected Label createLabel(Composite parent, FormToolkit toolkit) {
+		return toolkit.createLabel( parent, "Specify which packages this bundle shares with other bundles." );
+	}
+
+	@Override
+	protected IContentProvider getContentProvider() {
+        return new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return getBundle().getBundleInfo().getExports().toArray();
+			}
+	    };
+	}
+
+	@Override
+	protected void handleAdd() {
+		NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Add Exported Package", null, getProjectModel(), true);
+
+		if ( dialog.open() == Window.OK ) {
+			try {
+				// Add selected exports
+				boolean exportsAdded = false;
+				
+				List<IPackageFragment> newPkgFragments = dialog.getSelectedElements();
+				for (IPackageFragment pkgFragment : newPkgFragments) {
+					IPackageExport pkgExport = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+					pkgExport.setPackageName(pkgFragment.getElementName());
+					pkgExport.setVersion(dialog.getVersion());
+					getBundle().getBundleInfo().addExport(pkgExport);
+					
+					exportsAdded = true;
+				}
+
+				// Add corresponding imports (maybe)
+				boolean importsAdded = false;
+				
+				IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+				boolean shouldAddImports = OptionalPrompt.optionallyPrompt(store, SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Exports", "Should corresponding imports be added?", getSection().getShell());
+				if(shouldAddImports) {
+					for (IPackageFragment pkgFragment : newPkgFragments) {
+						IPackageImport pkgImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+						pkgImport.setPackageName(pkgFragment.getElementName());
+						VersionRangeBoundingRule lowerBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+						VersionRangeBoundingRule upperBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+						Version version = dialog.getVersion();
+						if(version == null) {
+							version = getBundle().getVersion();
+						}
+						VersionRange versionRange = VersionRange.newInstance(version, lowerBound, upperBound);
+						pkgImport.setVersions(versionRange);
+						
+						getBundle().getBundleInfo().addImport(pkgImport);
+						
+						importsAdded = true;
+					}
+				}
+				
+				if(importsAdded) {
+					((SigilProjectEditorPart) getPage().getEditor()).refreshAllPages();
+					markDirty();
+				} else if(exportsAdded) {
+					refresh();
+					markDirty();
+				}
+			}
+			catch (ModelElementFactoryException e) {
+				SigilCore.error( "Failed to buiild model element for package export", e);
+			}
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void handleEdit() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+		
+		boolean changed = false;
+		
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {	
+				IPackageExport packageExport = i.next();
+				NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Edit Imported Package", packageExport, getProjectModel(), false);
+				if ( dialog.open() == Window.OK ) {
+					changed = true;
+					IPackageFragment pkgFragment = dialog.getSelectedElement();
+					packageExport.setPackageName(pkgFragment.getElementName());
+					packageExport.setVersion(dialog.getVersion());
+				}
+			}
+		}
+		
+		if ( changed ) {
+			refresh();
+			markDirty();
+		}
+	}
+
+	@SuppressWarnings("unchecked") 
+	@Override
+	protected void handleRemoved() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {			
+				getBundle().getBundleInfo().removeExport( i.next() );
+			}		
+			
+			refresh();
+			markDirty();
+		}
+	}
+		
+	private ISigilBundle getBundle() {
+		return getProjectModel().getBundle();
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
new file mode 100644
index 0000000..7a6fe3c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ExportsForm extends SigilPage  {
+
+	public static final String PAGE_ID = "exports";
+	
+	private ISigilProjectModel project;
+	
+	public ExportsForm(FormEditor editor, ISigilProjectModel project) {
+		super(editor, PAGE_ID, "Exports");
+		this.project = project;
+	}
+	
+    @Override
+    protected void createFormContent(IManagedForm managedForm) {
+        ScrolledForm form = managedForm.getForm();
+        form.setText( "Exports" );
+        
+        Composite body = form.getBody();        
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.bottomMargin = 10;
+        layout.topMargin = 5;
+        layout.leftMargin = 10;
+        layout.rightMargin = 10;
+        layout.numColumns = 1;
+        layout.horizontalSpacing = 10;
+        layout.verticalSpacing = 20;
+        body.setLayout(layout);
+        body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+                       
+        try {
+	        ExportPackagesSection exportPackages = new ExportPackagesSection( this, body, project );
+	        managedForm.addPart( exportPackages );
+        }
+        catch (CoreException e) {
+        	SigilCore.error( "Failed to create contents form", e);
+        }
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
new file mode 100644
index 0000000..4200712
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.IFormValueConverter;
+import org.cauldron.sigil.ui.form.SigilFormEntry;
+import org.cauldron.sigil.ui.form.SigilFormEntryAdapter;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class GeneralInfoSection extends SigilSection {
+
+	private String name;
+	private String symbolicName;
+    private Version version;
+    private String description;
+    private String provider;
+    private String activator;
+    private IRequiredBundle fragmentHost;
+    
+    private SigilFormEntry nameEntry;
+	private SigilFormEntry symbolicNameEntry;
+    private SigilFormEntry versionEntry;
+    private SigilFormEntry descriptionEntry;
+    private SigilFormEntry providerEntry;
+    private SigilFormEntry activatorEntry;
+    private SigilFormEntry fragmentHostEntry;
+    
+    /**
+     * @param parent
+     * @param toolkit
+     * @param style
+     * @throws CoreException 
+     */
+    public GeneralInfoSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}    
+
+    protected void createSection(Section section,FormToolkit toolkit ) {
+        setTitle("General Information");
+                
+		Composite body = createGridBody(3, false, toolkit);
+        
+        Label label = toolkit.createLabel( body, "This section describes general information about this project." );
+        label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, false, 3, 1) );
+       
+        symbolicNameEntry = new SigilFormEntry(body, toolkit, "Symbolic Name");
+        symbolicNameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				symbolicName = nullIfEmpty((String) form.getValue());
+				checkDirty();
+			}
+        });
+        
+        nameEntry = new SigilFormEntry(body, toolkit, "Name");
+        nameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				name = nullIfEmpty((String) form.getValue());
+				checkDirty();
+			}
+        });
+        
+        descriptionEntry = new SigilFormEntry(body, toolkit, "Description");
+        descriptionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				description = nullIfEmpty((String) form.getValue());
+				checkDirty();
+			}
+        });
+        
+        IFormValueConverter converter = new IFormValueConverter() {
+			public String getLabel(Object value) {
+				Version v = (Version) value;
+				return v.toString();
+			}
+
+			public Object getValue(String label) {
+				return Version.parseVersion(label);
+			}        	
+        };
+        
+        versionEntry = new SigilFormEntry(body, toolkit, "Version", null, converter);
+        versionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				version = (Version) form.getValue();
+				checkDirty();
+			}
+        });
+        
+        providerEntry = new SigilFormEntry(body, toolkit, "Provider");
+        providerEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				provider = nullIfEmpty((String) form.getValue());
+				checkDirty();
+			}
+        });
+
+		activatorEntry = new SigilFormEntry(body, toolkit, "Bundle Activator", "Browse...", null);
+		activatorEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				activator = (String) form.getValue();
+				checkDirty();
+			}
+
+			@Override
+			public void browseButtonSelected(SigilFormEntry form) {
+				BackgroundLoadingSelectionDialog<String> dialog = ResourcesDialogHelper.createClassSelectDialog(getShell(), "Add Bundle Activator", getProjectModel(), activator, BundleActivator.class.getName());
+				
+				if (dialog.open() == Window.OK) {
+					form.setValue(dialog.getSelectedElement());
+				}				
+			}			
+		});
+		
+		converter = new IFormValueConverter() {
+			public String getLabel(Object value) {
+				IRequiredBundle b = (IRequiredBundle) value;
+				return b == null ? null : b.getSymbolicName() + " " + b.getVersions();
+			}
+
+			public Object getValue(String label) {
+				return null;
+			}			
+		};
+		
+		fragmentHostEntry = new SigilFormEntry(body, toolkit, "Fragment Host", "Browse...", converter );
+		fragmentHostEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+			@Override
+			public void textValueChanged(SigilFormEntry form) {
+				fragmentHost = (IRequiredBundle) form.getValue();
+				checkDirty();
+			}
+
+			@Override
+			public void browseButtonSelected(SigilFormEntry form) {
+				NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+				
+				if (dialog.open() == Window.OK) {
+					IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+					required.setSymbolicName(dialog.getSelectedName());
+					required.setVersions(dialog.getSelectedVersions());				
+					form.setValue(required);
+				}				
+			}			
+		});
+		fragmentHostEntry.setFreeText(false);
+    }
+    
+	private static String nullIfEmpty(String value) {
+		if ( value.trim().length() == 0 ) {
+			return null;
+		}
+		return value;
+	}
+	
+	private Shell getShell() {
+		return getSection().getShell();
+	}
+
+	@Override
+	public void commit(boolean onSave) {
+		getBundle().getBundleInfo().setSymbolicName( symbolicName );
+		getBundle().getBundleInfo().setName( name );
+		getBundle().getBundleInfo().setVersion( version );
+		getBundle().getBundleInfo().setDescription( description );
+		getBundle().getBundleInfo().setVendor( provider );
+		getBundle().getBundleInfo().setFragmentHost(fragmentHost);
+		getBundle().getBundleInfo().setActivator(activator);
+		
+		super.commit(onSave);
+	}
+
+	@Override
+	public void refresh() {
+        symbolicName = getProjectModel().getBundle().getBundleInfo().getSymbolicName();
+        name = getProjectModel().getBundle().getBundleInfo().getName();
+        description = getProjectModel().getBundle().getBundleInfo().getDescription();
+        version = getProjectModel().getBundle().getBundleInfo().getVersion();
+        provider = getProjectModel().getBundle().getBundleInfo().getVendor();
+        fragmentHost = getProjectModel().getBundle().getBundleInfo().getFragmentHost();
+        activator = getProjectModel().getBundle().getBundleInfo().getActivator();
+        
+        nameEntry.setValue(name);
+    	symbolicNameEntry.setValue(symbolicName);
+    	versionEntry.setValue(version);
+        descriptionEntry.setValue(description);
+        providerEntry.setValue(provider);
+        fragmentHostEntry.setValue(fragmentHost);
+        activatorEntry.setValue(activator);
+        
+		super.refresh();
+	}
+
+	private void checkDirty() {
+		boolean dirty = different(symbolicName, getProjectModel().getBundle().getBundleInfo().getSymbolicName() ) ||
+						different(name, getProjectModel().getBundle().getBundleInfo().getName() ) ||
+						different(version, getProjectModel().getBundle().getBundleInfo().getVersion() ) ||
+						different(description, getProjectModel().getBundle().getBundleInfo().getDescription()) ||
+						different(provider, getProjectModel().getBundle().getBundleInfo().getVendor()) ||
+						different(fragmentHost, getProjectModel().getBundle().getBundleInfo().getFragmentHost()) ||
+						different(activator, getProjectModel().getBundle().getBundleInfo().getActivator());
+				
+		if ( dirty ) markDirty();
+	}
+	
+	private boolean different(Object val1, Object val2) {
+		return val1 == null ? val2 != null : !val1.equals( val2 );
+	}
+
+	private ISigilBundle getBundle() {
+		return getProjectModel().getBundle();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
new file mode 100644
index 0000000..556c5e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IDependencyChecker {
+	boolean isSatisfied(IPackageImport packageImport);
+	boolean isSatisfied(IRequiredBundle requiredBundle);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
new file mode 100644
index 0000000..4aa2953
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+public interface IElementDescriptor<E> {	
+	/**
+	 * Return the short identifying name of the element.
+	 */
+	String getName(E element);
+
+	/**
+	 * Return a label for the element, including the name but possibly supplying
+	 * additional information.
+	 * 
+	 * @param element
+	 * @return
+	 */
+	String getLabel(E element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
new file mode 100644
index 0000000..9403075
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class ImportPackagesSection extends BundleDependencySection {
+	
+	public ImportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedPackages) throws CoreException {
+		super( page, parent, project, unresolvedPackages );
+	}
+
+	@Override
+	protected String getTitle() {
+		return "Import Packages";
+	}
+	
+	@Override
+	protected Label createLabel(Composite parent, FormToolkit toolkit) {
+		return toolkit.createLabel( parent, "Specify which packages this bundle imports from other bundles." );
+	}
+
+
+	@Override
+	protected IContentProvider getContentProvider() {
+		return new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				ArrayList<IPackageImport> imports = new ArrayList<IPackageImport>(getBundle().getBundleInfo().getImports());
+				Collections.sort(imports, new Comparator<IPackageImport>() {
+					public int compare(IPackageImport o1, IPackageImport o2) {
+						return o1.getPackageName().compareTo( o2.getPackageName() );
+					}
+				});
+				return imports.toArray();
+			}
+        };
+	}
+
+	protected ISigilBundle getBundle() {
+		return getProjectModel().getBundle();
+	}
+
+	@Override
+	protected void handleAdd() {
+		NewResourceSelectionDialog<? extends IPackageModelElement> dialog = 
+			ResourcesDialogHelper.createImportDialog(
+					getSection().getShell(), 
+					"Add Imported Package", 
+					getProjectModel(), 
+					null, 
+					getBundle().getBundleInfo().getImports() );
+		
+		if ( dialog.open() == Window.OK ) {
+			IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+			pi.setPackageName(dialog.getSelectedName());
+			pi.setVersions(dialog.getSelectedVersions());
+			pi.setOptional(dialog.isOptional());
+			
+			getBundle().getBundleInfo().addImport(pi);
+			refresh();
+			markDirty();
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void handleRemoved() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {			
+				getBundle().getBundleInfo().removeImport( i.next() );
+			}		
+			
+			refresh();
+			markDirty();
+		}
+	}
+	
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void handleEdit() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+		
+		boolean changed = false;
+		
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {	
+				IPackageImport packageImport = i.next();
+				NewResourceSelectionDialog<? extends IPackageModelElement> dialog = 
+					ResourcesDialogHelper.createImportDialog( 
+							getSection().getShell(), 
+							"Edit Imported Package", 
+							getProjectModel(), 
+							packageImport, 
+							getBundle().getBundleInfo().getImports() );
+				if ( dialog.open() == Window.OK ) {
+					changed = true;
+					IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+					newImport.setPackageName(dialog.getSelectedName());
+					newImport.setVersions(dialog.getSelectedVersions());
+					newImport.setOptional(dialog.isOptional());
+					
+					getBundle().getBundleInfo().removeImport( packageImport );
+					getBundle().getBundleInfo().addImport(newImport);
+				}
+			}					
+		}
+		
+		if ( changed ) {
+			refresh();
+			markDirty();
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
new file mode 100644
index 0000000..02289d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Comparator;
+
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class NewPackageExportDialog extends BackgroundLoadingSelectionDialog<IPackageFragment> {
+	
+	private static final IElementDescriptor<IPackageFragment> PKG_FRAGMENT_STRINGIFIER = new IElementDescriptor<IPackageFragment>() {
+		public String getLabel(IPackageFragment element) {
+			return getName(element);
+		}
+		public String getName(IPackageFragment element) {
+			return element.getElementName();
+		}
+	};
+	
+	private static final Comparator<IPackageFragment> PKG_FRAGMENT_COMPARATOR = new Comparator<IPackageFragment>() {
+		public int compare(IPackageFragment o1, IPackageFragment o2) {
+			return o1.getElementName().compareTo(o2.getElementName());
+		}
+	};
+	
+	private Version version = null;
+	private String error = null;
+	private Version projectVersion = new Version(0,0,0);
+	
+	private Button btnInheritBundleVersion;
+	private Button btnExplicitVersion;
+	private Text txtVersion;
+
+	
+	public NewPackageExportDialog(Shell parentShell, boolean multiSelect) {
+		super(parentShell, "Package:", multiSelect);
+		setDescriptor(PKG_FRAGMENT_STRINGIFIER);
+		setComparator(PKG_FRAGMENT_COMPARATOR);
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		// Create controls
+		Composite container = (Composite) super.createDialogArea(parent);
+		Composite composite = new Composite(container, SWT.NONE);
+		
+		Group grpVersion = new Group(composite, SWT.NONE);
+		grpVersion.setText("Version");
+		
+		btnInheritBundleVersion = new Button(grpVersion, SWT.RADIO);
+		btnInheritBundleVersion.setText("Inherit bundle version");
+		new Label(grpVersion, SWT.NONE); // Spacer
+		btnExplicitVersion = new Button(grpVersion, SWT.RADIO);
+		btnExplicitVersion.setText("Fixed version:");
+		txtVersion = new Text(grpVersion, SWT.BORDER);
+		
+		// Initialize
+		if(version == null) {
+			btnInheritBundleVersion.setSelection(true);
+			txtVersion.setEnabled(false);
+			txtVersion.setText(projectVersion.toString());
+		} else {
+			btnExplicitVersion.setSelection(true);
+			txtVersion.setEnabled(true);
+			txtVersion.setText(version.toString());
+		}
+		updateButtons();
+		
+		// Listeners
+		Listener radioAndTextListener = new Listener() {
+			public void handleEvent(Event event) {
+				error = null;
+				if(btnInheritBundleVersion.getSelection()) {
+					version = null;
+					txtVersion.setEnabled(false);
+				} else {
+					txtVersion.setEnabled(true);
+					try {
+						version = new Version(txtVersion.getText());
+					} catch (IllegalArgumentException e) {
+						error = "Invalid version";
+					}
+				}
+				setErrorMessage(error);
+				updateButtons();
+			}
+		};
+		txtVersion.addListener(SWT.Modify, radioAndTextListener);
+		btnInheritBundleVersion.addListener(SWT.Selection, radioAndTextListener);
+		btnExplicitVersion.addListener(SWT.Selection, radioAndTextListener);
+		
+		
+		// Layout
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		composite.setLayout(new GridLayout(1, false));
+		grpVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		grpVersion.setLayout(new GridLayout(2, false));
+		txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		
+		return container;
+	}
+	
+	@Override
+	protected boolean canComplete() {
+		return super.canComplete() && error == null;
+	}
+	
+	public void setProjectVersion(Version projectVersion) {
+		this.projectVersion = projectVersion;
+	}
+
+	public void setVersion(Version version) {
+		this.version = version;
+	}
+	
+	public Version getVersion() {
+		return version;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
new file mode 100644
index 0000000..59812cc
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IVersionedModelElement;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.Version;
+
+public class NewResourceSelectionDialog<E extends IVersionedModelElement> extends BackgroundLoadingSelectionDialog<E> {
+
+	private VersionRangeComponent pnlVersionRange;
+	private boolean optionalEnabled = true;
+	private Button btnOptional;
+
+	private VersionRange selectedVersions = null;
+	private boolean optional = false;
+
+	public NewResourceSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+		super(parentShell, selectionLabel, multi);
+	}
+	
+	public void setOptionalEnabled(boolean enabled) {
+		optionalEnabled = enabled;
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		// Create controls
+		Composite container = (Composite) super.createDialogArea(parent);
+		Composite composite = new Composite(container, SWT.NONE);
+		
+		if ( optionalEnabled ) {
+			new Label(composite, SWT.NONE); //Spacer
+			btnOptional = new Button(composite, SWT.CHECK);
+			btnOptional.setText("Optional");
+		}
+		
+		Label lblVersionRange = new Label(composite, SWT.NONE);
+		lblVersionRange.setText("Version Range:");
+		Group group = new Group(composite, SWT.BORDER);
+
+		pnlVersionRange = new VersionRangeComponent(group, SWT.NONE);
+		
+		// Initialize
+		if (selectedVersions != null) {
+			pnlVersionRange.setVersions(selectedVersions);
+		}
+		
+		if ( optionalEnabled ) {		
+			btnOptional.setSelection(optional);
+			updateButtons();
+		}
+		
+		// Hookup Listeners
+		pnlVersionRange.addVersionChangeListener(new VersionsChangeListener() {
+			public void versionsChanged(VersionRange range) {
+				selectedVersions = range;
+				updateButtons();
+			}
+		});
+		pnlVersionRange.addValidationListener(new IValidationListener() {
+			public void validationMessage(String message, int level) {
+				setMessage(message, level);
+				updateButtons();
+			}
+		});
+		
+		if ( optionalEnabled ) {		
+			btnOptional.addSelectionListener(new SelectionAdapter() {
+				public void widgetSelected(SelectionEvent e) {
+					optional = btnOptional.getSelection();
+				}
+			});
+		}
+		
+		// Layout
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		GridLayout layout = new GridLayout(2, false);
+		layout.verticalSpacing = 10;
+		layout.horizontalSpacing = 10;
+		composite.setLayout(layout);
+
+		lblVersionRange.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+		group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		group.setLayout(new FillLayout());
+
+		return container;
+	}
+
+	@Override
+	protected void elementSelected(E selection) {
+		if(selection != null) {
+			IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+			VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+			VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+			
+			Version version = selection.getVersion();
+			selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+			pnlVersionRange.setVersions(selectedVersions);
+		}
+	}
+
+	@Override
+	protected synchronized boolean canComplete() {
+		return super.canComplete() && selectedVersions != null;
+	}
+
+	public VersionRange getSelectedVersions() {
+		return selectedVersions;
+	}
+
+	public void setVersions(VersionRange versions) {
+		selectedVersions = versions;
+		if(pnlVersionRange != null && !pnlVersionRange.isDisposed()) {
+			pnlVersionRange.setVersions(versions);
+			updateButtons();
+		}
+	}
+	
+	public boolean isOptional() {
+		return optional;
+	}
+	
+	public void setOptional(boolean optional) {
+		this.optional = optional;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
new file mode 100644
index 0000000..a948427
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+/**
+ * @author dave
+ *
+ */
+public class OverviewForm extends SigilPage {
+    public static final String PAGE_ID = "overview";
+    private ISigilProjectModel sigil;
+    
+    public OverviewForm(SigilProjectEditorPart editor, ISigilProjectModel sigil) {
+        super(editor, PAGE_ID, "Overview");
+        this.sigil = sigil;
+    }
+
+    @Override
+    protected void createFormContent(IManagedForm managedForm) {
+        FormToolkit toolkit = managedForm.getToolkit();
+        
+        ScrolledForm form = managedForm.getForm();
+        form.setText( "Overview" );
+        
+        Composite body = form.getBody();        
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.bottomMargin = 10;
+        layout.topMargin = 5;
+        layout.leftMargin = 10;
+        layout.rightMargin = 10;
+        layout.numColumns = 2;
+        layout.horizontalSpacing = 10;
+        body.setLayout(layout);
+        body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+        
+        Composite left = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        left.setLayout(layout);
+        left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+        
+        Composite right = toolkit.createComposite(body);
+        layout = new TableWrapLayout();
+        layout.verticalSpacing = 20;
+        right.setLayout(layout);
+        right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+        
+        try {
+	        GeneralInfoSection general = new GeneralInfoSection(this, left, sigil);
+	        managedForm.addPart( general );
+	        
+	        ContentSummarySection content = new ContentSummarySection( this, right, sigil);
+	        managedForm.addPart( content );
+	        
+	        // XXX-FELIX
+	        // commented out due to removal of runtime newton integration
+	        // potential to bring back in medium term...
+	        //TestingSection testing = new TestingSection(this, right, newton);
+	        //managedForm.addPart(testing);
+	        
+	        ToolsSection tools = new ToolsSection(this, right, sigil);
+	        managedForm.addPart(tools);
+        }
+        catch (CoreException e) {
+        	SigilCore.error( "Failed to create overview form", e );
+        }
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
new file mode 100644
index 0000000..80c2328
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class PackageExportDialog extends ResourceSelectDialog  {
+
+	private Text versionText;
+	private Version version;
+	
+	public PackageExportDialog(Shell parentShell, String title, IContentProvider content, ViewerFilter filter, Object scope ) {
+		super(parentShell, content, filter, scope, title, "Package Name:", true);
+	}
+	
+	@Override
+	protected void createCustom(Composite body) {
+		Label l = new Label( body, SWT.NONE );
+		l.setText( "Version:" );
+		versionText = new Text(body, SWT.BORDER);
+		versionText.addKeyListener(new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				try {
+					version = Version.parseVersion(versionText.getText());
+					setErrorMessage(null);
+				}
+				catch (IllegalArgumentException ex) {
+					setErrorMessage("Invalid version");
+				}
+			}			
+		});
+		if ( version != null ) {
+			versionText.setText( version.toString() );
+		}
+	}
+
+	public Version getVersion() {
+		return version;
+	}
+
+	public void setVersion(Version version) {
+		this.version = version;
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
new file mode 100644
index 0000000..ca745f5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.InputStream;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Widget;
+
+public class ProjectLabelProvider extends DefaultLabelProvider implements IBaseLabelProvider {
+
+	private final Widget parent;
+	private final ImageRegistry registry;
+	private final IDependencyChecker checker;
+	
+	public ProjectLabelProvider(Widget parent, ImageRegistry registry ) {
+		this(parent, registry, null);
+	}
+	
+	public ProjectLabelProvider(Widget parent, ImageRegistry registry, IDependencyChecker checker) {
+		this.parent = parent;
+		this.registry = registry;
+		this.checker = checker;
+	}
+
+	public Image getImage(Object element) {
+		Image result = null;
+		if ( element instanceof ISigilBundle || element instanceof IRequiredBundle || element instanceof IBundleModelElement) {
+			result = findBundle();
+		}
+		else if (element instanceof IPackageImport) {
+			if(checker != null) {
+				result = checker.isSatisfied((IPackageImport) element) ? findPackage() : findPackageError();
+			} else {
+				result = findPackage();
+			}
+		}
+		else if (element instanceof IPackageExport) {
+			result = findPackage();
+		}
+		else if ( element instanceof IPackageFragmentRoot ) {
+			IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+			try {
+				if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+					result = findPackage();
+				}
+				else {
+					result = findBundle();
+				}
+			} catch (JavaModelException e) {
+				SigilCore.error( "Failed to inspect package fragment root", e );
+			}
+		}
+		else if ( element instanceof IClasspathEntry ) {
+			result = findPackage();
+		}
+	
+		return result;
+	}
+
+	public String getText(Object element) {
+		if ( element instanceof ISigilBundle ) {
+			ISigilBundle bundle = (ISigilBundle) element;
+			return bundle.getBundleInfo().getSymbolicName(); 
+		}
+
+		if ( element instanceof IRequiredBundle ) {
+			IRequiredBundle req = (IRequiredBundle) element;
+			return req.getSymbolicName() + " " + req.getVersions();
+		}
+		
+		if ( element instanceof IPackageImport ) {
+			IPackageImport req = (IPackageImport) element;
+			return req.getPackageName() + " " + req.getVersions();
+		}
+		
+		if ( element instanceof IPackageExport ) {
+			IPackageExport pe = (IPackageExport) element;
+			return pe.getPackageName() + " " + pe.getVersion();
+		}
+		
+		if ( element instanceof IResource ) {
+			IResource resource = (IResource) element;
+			return resource.getName();
+		}
+		
+		if ( element instanceof IPackageFragment )  {
+			IPackageFragment f = (IPackageFragment) element;
+			return f.getElementName();
+		}
+		
+		if ( element instanceof IPackageFragmentRoot ) {
+			IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+			try {
+				return f.getUnderlyingResource().getName();
+			} catch (JavaModelException e) {
+				return "unknown";
+			}
+		}
+		
+		if ( element instanceof IClasspathEntry ) {
+			IClasspathEntry cp = (IClasspathEntry) element;
+			return cp.getPath().toString();
+		}
+		
+		return element.toString();
+	}
+
+	private Image findPackage() {
+		Image image = registry.get( "package" );
+		
+		if ( image == null ) {
+			image = loadImage( "icons/package_obj.png" ); 
+			registry.put( "package", image);
+		}
+		
+		return image; 
+	}
+	
+	private Image findPackageError() {
+		Image image = registry.get("package_error");
+		if(image == null) {
+			image = loadImage("icons/package_obj_error.gif");
+			registry.put("package_error", image);
+		}
+		return image;
+	}
+
+	private Image findBundle() {
+		Image image = registry.get( "bundle" );
+		
+		if ( image == null ) {
+			image = loadImage( "icons/jar_obj.png" ); 
+			registry.put( "bundle", image);
+		}
+		
+		return image; 
+	}
+		
+	private Image loadImage(String resource) {
+		InputStream in = ProjectLabelProvider.class.getClassLoader().getResourceAsStream( resource );
+		ImageData data = new ImageData( in );
+		return new Image( parent.getDisplay(), data );
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
new file mode 100644
index 0000000..362de5b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.widgets.Table;
+
+public class ProjectTableViewer extends TableViewer {
+
+	private ModelLabelProvider labelProvider;
+
+	public ProjectTableViewer(Table table) {
+		super(table);
+		labelProvider = new ModelLabelProvider();
+		setLabelProvider(labelProvider);
+	}
+
+	@Override
+	public void setContentProvider(IContentProvider provider) {
+		super.setContentProvider(provider);
+		setInput(getTable());
+	}
+
+	public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+		labelProvider.setUnresolvedElements(elements);
+	}
+
+	@Override
+	public ModelLabelProvider getLabelProvider() {
+		return labelProvider;
+	}
+	
+	
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
new file mode 100644
index 0000000..1e069fd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.IPropertiesFilePartitions;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.PropertiesFileSourceViewerConfiguration;
+import org.eclipse.jdt.ui.text.JavaTextTools;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+@SuppressWarnings("restriction")
+public class PropertiesForm extends SigilSourcePage {
+
+    public static final String PAGE_ID = "properties";
+    
+	public PropertiesForm(SigilProjectEditorPart editor, ISigilProjectModel project) {
+		super(PAGE_ID);
+		JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
+		IPreferenceStore store= JavaPlugin.getDefault().getCombinedPreferenceStore();
+		setPreferenceStore(store);
+		setSourceViewerConfiguration(new PropertiesFileSourceViewerConfiguration(textTools.getColorManager(), store, this, IPropertiesFilePartitions.PROPERTIES_FILE_PARTITIONING));
+        /*IFileEditorInput fileInput = (IFileEditorInput) editor.getEditorInput();
+		this.setDocumentProvider(fileInput);*/
+	}
+
+	@Override
+	public boolean isSaveAsAllowed() {
+		return false;
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
new file mode 100644
index 0000000..5adf84d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class RequiresBundleSection extends BundleDependencySection {
+	
+	public RequiresBundleSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+		super( page, parent, project, unresolvedElements );
+	}
+	
+	@Override
+	protected String getTitle() {
+		return "Requires Bundles";
+	}
+	
+	@Override
+	protected Label createLabel(Composite parent, FormToolkit toolkit) {
+		return toolkit.createLabel( parent, "Specify which bundles this bundle depends on." );
+	}
+	
+	@Override
+	protected IContentProvider getContentProvider() {
+		return new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return getBundle().getBundleInfo().getRequiredBundles().toArray();
+			}
+		};
+	}
+
+	protected ISigilBundle getBundle() {
+		return getProjectModel().getBundle();
+	}
+
+	@Override
+	protected void handleAdd() {
+		try {
+			NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+			
+			if (dialog.open() == Window.OK) {
+				IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+				required.setSymbolicName(dialog.getSelectedName());
+				required.setVersions(dialog.getSelectedVersions());
+				required.setOptional(dialog.isOptional());
+				
+				getBundle().getBundleInfo().addRequiredBundle(required);
+				refresh();
+				markDirty();
+			}
+		}
+		catch (ModelElementFactoryException e) {
+			SigilCore.error( "Failed to build required bundle", e );
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void handleEdit() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+		
+		boolean changed = false;
+		
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {	
+				IRequiredBundle requiredBundle = i.next();
+				NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog(getSection().getShell(), "Edit Imported Package", getProjectModel(), requiredBundle, getBundle().getBundleInfo().getRequiredBundles() );
+				if ( dialog.open() == Window.OK ) {
+					changed = true;
+					requiredBundle.setSymbolicName(dialog.getSelectedName());
+					requiredBundle.setVersions(dialog.getSelectedVersions());
+					requiredBundle.setOptional(dialog.isOptional());
+				}
+			}					
+		}
+		
+		if ( changed ) {
+			refresh();
+			markDirty();
+		}
+	}
+	
+	@SuppressWarnings("unchecked")
+	@Override
+	protected void handleRemoved() {
+		IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {			
+				getBundle().getBundleInfo().removeRequiredBundle( i.next() );
+			}		
+			
+			refresh();
+			markDirty();
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
new file mode 100644
index 0000000..efd61d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class ResourceBuildSection extends AbstractResourceSection implements ICheckStateListener, IResourceChangeListener, IPropertyChangeListener {
+
+	private ExcludedResourcesFilter resourcesFilter;
+
+	/**
+	 * @param page
+	 * @param parent
+	 * @param project
+	 * @throws CoreException 
+	 */
+	public ResourceBuildSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}
+
+	/* (non-Javadoc)
+	 * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+	 */
+	@Override
+	protected void createSection(Section section, FormToolkit toolkit) {
+		setTitle( "Resources" );
+		
+		Composite body = createTableWrapBody(1, toolkit);
+
+        toolkit.createLabel( body, "Specify which resources are included in the bundle." );
+		
+		tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+		Link link = new Link(body, SWT.WRAP);
+		link.setText("Some resources may be filtered according to preferences. <a href=\"excludedResourcePrefs\">Click here</a> to edit the list of exclusions.");
+		
+		TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+		data.heightHint = 200;
+		tree.setLayoutData( data );
+		
+		viewer = new CheckboxTreeViewer( tree );
+		IProject base = getProjectModel().getProject();
+		viewer.setContentProvider( new ContainerTreeProvider() );
+		viewer.setLabelProvider( new ModelLabelProvider() );
+		viewer.addCheckStateListener( this );
+		resourcesFilter = new ExcludedResourcesFilter();
+		viewer.addFilter(resourcesFilter);
+		viewer.setInput(base);
+		
+		link.addListener(SWT.Selection, new Listener() {
+			public void handleEvent(Event event) {
+				if("excludedResourcePrefs".equals(event.text)) {
+					PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(getPage().getEditorSite().getShell(), SigilCore.EXCLUDED_RESOURCES_PREFERENCES_ID, null, null);
+					dialog.open();
+				}
+			}
+		});
+		
+		SigilCore.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+		
+		startWorkspaceListener(base.getWorkspace());
+	}
+
+	@Override
+	public void commit(boolean onSave) {
+		ISigilBundle bundle = getProjectModel().getBundle();
+		
+		bundle.clearSourcePaths();
+		
+		SigilUI.runInUISync( new Runnable() {
+			public void run() {
+				for ( Object o : viewer.getCheckedElements() ) {
+					if ( !viewer.getGrayed(o) ) {
+						IResource r = (IResource) o;
+						getProjectModel().getBundle().addSourcePath( r.getProjectRelativePath() );
+					}
+				}
+			}			
+		});
+		
+		super.commit(onSave);
+	}
+
+	@Override
+	protected void refreshSelections()  {
+		// zero the state
+		for ( IPath path : getProjectModel().getBundle().getSourcePaths() ) {
+			IResource r = findResource( path );
+			if ( r != null ) {
+				viewer.expandToLevel(r, 0);
+				viewer.setChecked( r, true );
+				viewer.setGrayed( r, false );
+				handleStateChanged(r, true, false, false);
+			}
+			else {
+				SigilCore.error( "Unknown path " + path );
+			}
+		}
+	}
+	
+	@Override
+	protected void syncResourceModel(IResource element, boolean checked) {
+		if ( checked ) {
+			getProjectModel().getBundle().addSourcePath( element.getProjectRelativePath() );
+		}
+		else {
+			getProjectModel().getBundle().removeSourcePath( element.getProjectRelativePath() );
+		}
+		
+		markDirty();
+	}
+	
+	@Override
+	public void dispose() {
+		super.dispose();
+		SigilCore.getDefault().getPreferenceStore().removePropertyChangeListener(this);
+	}
+
+	public void propertyChange(PropertyChangeEvent event) {
+		resourcesFilter.loadExclusions();
+		viewer.refresh();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
new file mode 100644
index 0000000..e50fb21
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+public class ResourceImportDialog extends ResourceSelectDialog implements VersionsChangeListener {
+
+	private VersionRangeComponent versions;
+	private VersionRange range;
+	
+	public ResourceImportDialog(Shell parentShell, String title, String label, IContentProvider content, ViewerFilter filter, Object scope ) {
+		super(parentShell, content, filter, scope, title, label, true);
+	}
+
+	public VersionRange getVersions() {
+		return range;
+	}
+
+	@Override
+	protected void createCustom(Composite body) {
+		versions = new VersionRangeComponent(body, SWT.BORDER );
+		versions.addVersionChangeListener(this);
+		versions.setVersions(range);
+		
+		GridData data = new GridData( SWT.LEFT, SWT.TOP, true, true );
+		data.horizontalSpan = 2;
+		data.widthHint = 300;
+		data.heightHint = 200;
+		versions.setLayoutData(data);
+	}
+
+	@Override
+	protected void selectionChanged(SelectionChangedEvent event) {
+		if ( event.getSelection().isEmpty() ) {
+			versions.setEnabled(false);
+		}
+		else {
+			versions.setEnabled(true);
+		}
+	}
+
+	public void versionsChanged(VersionRange range) {
+		this.range = range; 
+		if ( range == null ) {
+			setErrorMessage( "Invalid version" );
+		}
+		else {
+			setErrorMessage( null );
+		}
+	}
+
+	public void setVersions(VersionRange range) {
+		this.range = range;
+	}
+
+	public IPackageImport getImport() {
+		IPackageImport packageImport = ModelElementFactory.getInstance().newModelElement( IPackageImport.class );
+		packageImport.setPackageName( (String) getSelected()[0] );
+		packageImport.setVersions( getVersions() );
+		return packageImport;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
new file mode 100644
index 0000000..e967548
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.cauldron.sigil.ui.util.SingletonSelection;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.StringConverter;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+
+public class ResourceSelectDialog extends Dialog {
+
+	private AtomicInteger keyCheck = new AtomicInteger();
+	private ScheduledExecutorService background = Executors.newSingleThreadScheduledExecutor();
+	
+	private class UpdateViewerRunnable implements Runnable {
+		private int check;
+		public UpdateViewerRunnable(int check) {
+			this.check = check;
+		}
+
+		public void run() {
+			if ( check == keyCheck.get() ) {
+				try {
+					viewer.refresh();
+				}
+				catch (SWTException e) {
+					// discard
+				}
+			}
+		}
+	}
+
+	private static final ISelection EMPTY_SELECTION = new ISelection() {
+		public boolean isEmpty() {
+			return true;
+		}
+	};
+	
+	private Job job;
+	
+	private boolean isCombo;
+	private String title;
+	private String selectionText;
+
+	private StructuredViewer viewer;
+	private Combo resourceNameCombo;
+	private Table resourceNameTable;
+	private Text errorMessageText;
+	private String errorMessage;
+	
+	private ViewerFilter filter;
+	private Object[] selected;
+	
+	private Object scope;	
+	private IContentProvider content;
+	private ILabelProvider labelProvider;
+
+	public ResourceSelectDialog(Shell parentShell, IContentProvider content, ViewerFilter filter, Object scope, String title, String selectionText, boolean isCombo) {
+		super(parentShell);
+		this.title = title;
+		this.selectionText = selectionText;
+		this.content = content;
+		this.filter = filter;
+		this.scope = scope;
+		this.isCombo = isCombo;
+	}
+	
+	public void setJob(Job job) {
+		this.job = job;
+	}
+	
+	public void refreshResources() {
+		try {
+			getShell().getDisplay().asyncExec( new Runnable() {
+				public void run() {
+					try {
+						viewer.refresh();
+					}
+					catch (SWTException e) {
+						// attempt to exec after dialog closed - discard
+					}
+				}
+			});
+		}
+		catch (NullPointerException e) {
+			// attempt to exec after dialog closed - discard
+		}
+		catch (SWTException e) {
+			// attempt to exec after dialog closed - discard
+		}
+	}
+
+	/*
+     * (non-Javadoc)
+     * 
+     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+     */
+    protected void configureShell(Shell shell) {
+        super.configureShell(shell);
+        if (title != null) {
+			shell.setText(title);
+		}        
+    }
+
+	@Override
+	public void create() {
+		super.create();
+		if ( getItemCount() == 0 ) {
+			setErrorMessage( "No resources available" );
+			getButton(IDialogConstants.OK_ID).setEnabled(false);
+		}
+		else {
+			ISelection selection = selected == null ? EMPTY_SELECTION : new SingletonSelection( selected );
+			setSelected( new SelectionChangedEvent( viewer, selection ), true );
+		}
+		
+		if ( job != null ) {
+			job.schedule();
+		}
+	}
+
+	@Override
+	public boolean close() {
+		if ( job != null ) {
+			job.cancel();
+		}
+		background.shutdownNow();
+		return super.close();
+	}
+
+	private int getItemCount() {
+		if ( isCombo ) {
+			return resourceNameCombo.getItemCount();
+		}
+		else {
+			return resourceNameTable.getItemCount();
+		}
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		Composite body = (Composite) super.createDialogArea(parent);
+		
+		GridLayout layout = (GridLayout) body.getLayout();
+		layout.numColumns = 2;
+		GridData data;
+		
+		labelProvider = new ModelLabelProvider();
+		
+		Label l = new Label( body, SWT.LEFT );
+		l.setText( selectionText );
+		data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+		if ( !isCombo ) {
+			data.horizontalSpan = 2;
+		}
+		l.setLayoutData( data );
+	
+		if ( isCombo ) {
+			createCombo( body );
+		}
+		else {
+			createTable( body );
+		}
+		
+		viewer.addFilter( filter );
+		viewer.setContentProvider(content);
+		viewer.setLabelProvider( getLabelProvider() );
+		viewer.setComparator( new ViewerComparator() );
+		viewer.setInput( scope );
+		
+		viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				setSelected(event, false);
+			}			
+		});
+	    createCustom( body );
+	    
+		errorMessageText = new Text(body, SWT.READ_ONLY | SWT.WRAP);
+		data = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
+		data.horizontalSpan = 2;
+	    errorMessageText.setLayoutData(data);
+	    errorMessageText.setBackground(errorMessageText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+	    setErrorMessage(errorMessage);
+	    
+		return body;
+	}
+	
+    protected void createCustom(Composite body) {
+	}
+
+	private void createCombo(Composite body) {
+		resourceNameCombo = new Combo( body, SWT.SINGLE | SWT.BORDER );
+		GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+		data.widthHint = 200;
+		resourceNameCombo.setLayoutData( data );
+		
+		viewer = new ComboViewer(resourceNameCombo);
+	}
+
+	private void createTable(Composite body) {
+		final Text txtFilter = new Text(body, SWT.BORDER);
+		GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+		data.horizontalSpan = 2;
+		data.widthHint = 400;
+		txtFilter.setLayoutData(data);
+		
+		resourceNameTable = new Table( body, SWT.MULTI | SWT.BORDER );
+		data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+		data.widthHint = 400;
+		data.heightHint = 400;
+		resourceNameTable.setLayoutData( data );
+		
+		viewer = new TableViewer(resourceNameTable);
+
+		txtFilter.addKeyListener( new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				switch ( e.keyCode ) {
+				case SWT.ARROW_UP:
+					scrollTable(-1);
+					break;
+				case SWT.ARROW_DOWN:
+					scrollTable(+1);
+					break;
+				default:
+					Runnable r = new UpdateViewerRunnable(keyCheck.incrementAndGet());
+					background.schedule(r, 100, TimeUnit.MILLISECONDS);
+					break;
+				}
+			}
+		});
+		
+		ViewerFilter filter = new ViewerFilter() {
+			@Override
+			public boolean select(Viewer viewer, Object parentElement,
+					Object element) {
+				return getLabelProvider().getText(element).startsWith( txtFilter.getText() );
+			}
+		};
+		
+		viewer.addFilter(filter);
+	}
+	
+	private void scrollTable(int delta) {
+		int i = resourceNameTable.getSelectionIndex();
+		
+		if ( i == -1 ) {
+			if ( delta < 0 ) { 
+				i = resourceNameTable.getItemCount() - 1;
+			}
+			else {
+				i = 0;
+			}
+		}
+		else {
+			i+=delta;
+		}
+		
+		if ( i > -1 && i < resourceNameTable.getItemCount() ) {
+			Item item = resourceNameTable.getItem(i);
+			resourceNameTable.select(i);
+			selected = new Object[] { item.getData() };
+			ISelection selection = new SingletonSelection( selected );
+			selectionChanged(new SelectionChangedEvent(viewer, selection));
+			viewer.reveal(selected);
+		}
+	}
+
+	private void setSelected(SelectionChangedEvent event, boolean reveal) {
+		if ( event.getSelection().isEmpty() ) {
+			selected = null;
+			setErrorMessage( "No resource selected" );
+		}
+		else {
+			selected = ((IStructuredSelection) event.getSelection()).toArray();
+			setErrorMessage( null );
+		}
+		
+		selectionChanged(event);
+		
+		if ( reveal && !event.getSelection().isEmpty() ) {
+			if ( resourceNameCombo != null ) {
+				IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+				resourceNameCombo.select(resourceNameCombo.indexOf((String) sel.getFirstElement()));
+			}
+			else {
+				viewer.setSelection(event.getSelection(), true);
+			}
+		}
+	}
+
+	protected ILabelProvider getLabelProvider() {
+		return labelProvider;
+	}
+
+	public Object[] getSelected() {
+    	return selected;
+    }
+	
+	public void setSelected(Object[] selected) {
+		this.selected = selected;
+	}
+	
+	protected void selectionChanged(SelectionChangedEvent event) {
+	}
+
+	public void setErrorMessage(String errorMessage) {
+		this.errorMessage = errorMessage;
+		if (errorMessageText != null && !errorMessageText.isDisposed()) {
+			errorMessageText.setText(errorMessage == null ? " \n " : errorMessage); 
+			boolean hasError = errorMessage != null && (StringConverter.removeWhiteSpaces(errorMessage)).length() > 0;
+			errorMessageText.setEnabled(hasError);
+			errorMessageText.setVisible(hasError);
+			errorMessageText.getParent().update();
+			Control ok = getButton(IDialogConstants.OK_ID);
+			if (ok != null) {
+				ok.setEnabled(!hasError);
+			}
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
new file mode 100644
index 0000000..c1fafaf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+
+public class SigilProjectEditorPart extends FormEditor implements IResourceChangeListener {
+
+	private final Set<IModelElement> unresolvedElements = Collections.synchronizedSet(new HashSet<IModelElement>());
+	private ISigilProjectModel project;
+	private volatile boolean saving = false;
+	private int dependenciesPageIndex;
+	
+	// XXX-FIXME-XXX
+	private Image errorImage = null; //SigilUI.imageDescriptorFromPlugin(SigilUI.PLUGIN_ID, "icons/error.gif").createImage();
+	private PropertiesForm textPage;
+	
+    public IProject getProject() {
+        IFileEditorInput fileInput = (IFileEditorInput) getEditorInput();
+        return fileInput.getFile().getProject();
+    }
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
+     */
+    @Override
+    public void doSave(IProgressMonitor monitor) {
+		monitor.beginTask("Saving", IProgressMonitor.UNKNOWN);
+		try {
+			saving = true;
+			new ProgressMonitorDialog(getSite().getShell()).run(true, true, new IRunnableWithProgress() {
+				public void run(final IProgressMonitor monitor) throws InvocationTargetException,
+						InterruptedException {
+					try {
+						if ( textPage.isDirty() ) {
+							SigilUI.runInUISync(new Runnable() {
+								public void run() {
+									textPage.doSave(monitor);
+								}								
+							});
+							project.setBundle(null);
+						}
+						else if ( isDirty() ) {
+							commitPages(true);
+							project.save(monitor);
+							SigilUI.runInUISync(new Runnable() {
+								public void run() {
+									editorDirtyStateChanged();
+								}								
+							});
+						}
+						
+						monitor.done();
+					} catch (CoreException e) {
+						throw new InvocationTargetException(e);
+					}
+				}
+			});
+		} catch (InvocationTargetException e) {
+			SigilCore.error("Failed to save " + project, e.getTargetException());
+		} catch (InterruptedException e) {
+			monitor.setCanceled(true);
+			return;
+		} finally {
+			saving = false;
+		}
+		monitor.done();		
+    }
+    
+	/* (non-Javadoc)
+     * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
+     */
+    @Override
+    protected void addPages() {
+        try {
+            addPage(new OverviewForm(this, project));
+			addPage(new ContentsForm(this, project));
+			dependenciesPageIndex = addPage(new DependenciesForm(this, project, unresolvedElements));
+			addPage(new ExportsForm(this, project));
+			textPage = new PropertiesForm(this, project);
+			addPage(textPage, getEditorInput());
+			setPartName(project.getSymbolicName());
+			
+			refreshTabImages();
+        }
+        catch (PartInitException e) {
+        	SigilCore.error( "Failed to build " + this, e );
+		}
+    }
+    
+    protected void refreshTabImages() {
+	    if(unresolvedElements.isEmpty()) {
+	    	setPageImage(dependenciesPageIndex, null);
+	    } else {
+	    	setPageImage(dependenciesPageIndex, errorImage);
+	    }
+    }
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.part.EditorPart#doSaveAs()
+     */
+    @Override
+    public void doSaveAs() {
+    	// save as not allowed
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
+     */
+    @Override
+    public boolean isSaveAsAllowed() {
+        return false;
+    }
+    
+    @Override
+    public void dispose() {
+    	ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+    	errorImage.dispose();
+    	super.dispose();
+    }
+
+	public void resourceChanged(IResourceChangeEvent event) {
+		IResourceDelta delta = event.getDelta();
+		final IFile editorFile = ((IFileEditorInput) getEditorInput()).getFile();
+		try {
+			delta.accept(new IResourceDeltaVisitor() {
+				public boolean visit(IResourceDelta delta) throws CoreException {
+					int kind = delta.getKind();
+					IResource resource = delta.getResource();
+					if(resource instanceof IProject) {
+						if(!editorFile.getProject().equals(resource)) {
+							return false;
+						}
+						if(kind == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.MARKERS) > 0) {
+							loadUnresolvedDependencies();
+							refreshAllPages();
+						}
+						return true;
+					}
+					
+					if(resource instanceof IFile) {
+						IFile affectedFile = (IFile) resource;
+							if(affectedFile.equals(editorFile)) {
+							switch (kind) {
+							case IResourceDelta.REMOVED:
+								close(false);
+								break;
+							case IResourceDelta.CHANGED:
+								if(!saving) {
+									reload();
+								}
+								SigilUI.runInUISync( new Runnable() {
+									public void run() {
+										setPartName(project.getSymbolicName());								
+									} 									
+								} );
+								break;
+							}
+						}
+						// Recurse no more
+						return false;
+					}
+					
+					return true;
+				}
+			});
+		} catch (CoreException e) {
+			ErrorDialog.openError(getSite().getShell(), "Error", null, e.getStatus());
+		}
+	}
+	
+	protected void refreshAllPages() {
+		Runnable op = new Runnable() {
+			public void run() {
+				for(Iterator<?> iter = pages.iterator(); iter.hasNext(); ) {
+					IFormPage page = (IFormPage) iter.next();
+					if(page != null) {
+						IManagedForm managedForm = page.getManagedForm();
+						if(managedForm != null) {
+							managedForm.refresh();
+							IFormPart[] parts = managedForm.getParts();
+							for (IFormPart part : parts) {
+								part.refresh();
+							}
+						}
+					}
+				}
+				firePropertyChange(IEditorPart.PROP_DIRTY);
+				setPartName(project.getSymbolicName());
+				refreshTabImages();
+			}
+		};
+		getSite().getShell().getDisplay().syncExec(op);
+	}
+	
+	protected void reload() {
+		project.setBundle(null);
+		refreshAllPages();
+	}
+	
+	@Override
+	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+		super.init(site, input);
+		
+        try {
+			this.project = SigilCore.create(getProject());
+		} catch (CoreException e) {
+			throw new PartInitException("Error creating Sigil project", e);
+		}
+		
+    	ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+		
+		if(input instanceof IFileEditorInput) {
+			try {
+				loadUnresolvedDependencies();
+			} catch (CoreException e) {
+				throw new PartInitException("Error retrieving dependency markers", e);
+			}
+		}
+	}
+
+	private void loadUnresolvedDependencies() throws CoreException {
+		ModelElementFactory factory = ModelElementFactory.getInstance();
+		IMarker[] markers = getProject().findMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE);
+		unresolvedElements.clear();
+		
+		for (IMarker marker : markers) {
+			String elementName = (String) marker.getAttribute("element");
+			String versionRangeStr = (String) marker.getAttribute("versionRange");
+			if(elementName != null && versionRangeStr != null) {
+				if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE)) {
+					IPackageImport pkgImport = factory.newModelElement(IPackageImport.class);
+					pkgImport.setPackageName(elementName);
+					pkgImport.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+					unresolvedElements.add(pkgImport);
+				} else if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE)) {
+					IRequiredBundle req = factory.newModelElement(IRequiredBundle.class);
+					req.setSymbolicName(elementName);
+					req.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+					unresolvedElements.add(req);
+				}
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
new file mode 100644
index 0000000..291c927
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+import org.eclipse.ui.ide.IDE;
+
+public class SigilSourcePage extends TextEditor implements IFormPage {
+	private final String id;
+	private int index;
+	private SigilProjectEditorPart editor;
+	private boolean active;
+	private Control control;
+	
+	public SigilSourcePage(String id) {
+		this.id = id;
+	}
+
+	@Override
+	public void createPartControl(Composite parent) {
+		super.createPartControl(parent);
+		Control[] children = parent.getChildren();
+		control = children[children.length - 1];
+		getSourceViewer().addTextListener( new ITextListener() {
+			public void textChanged(TextEvent event) {
+				if ( editor != null ) {
+					editor.refreshAllPages();
+				}
+			}
+		});
+		//PlatformUI.getWorkbench().getHelpSystem().setHelp(fControl, IHelpContextIds.MANIFEST_SOURCE_PAGE);
+	}
+
+	public void initialize(FormEditor editor) {
+		this.editor = (SigilProjectEditorPart) editor;
+	}
+
+	public FormEditor getEditor() {
+		return editor;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public int getIndex() {
+		return index;
+	}
+	
+	public void setIndex(int index) {
+		this.index = index;
+	}	
+
+	public boolean isActive() {
+		return active;
+	}
+
+	public void setActive(boolean active) {
+		this.active = active;
+	}
+	
+	public Control getPartControl() {
+		return control;
+	}
+
+	public boolean selectReveal(Object object) {
+		if (object instanceof IMarker) {
+			IDE.gotoMarker(this, (IMarker) object);
+			return true;
+		}
+		return false;
+	}
+
+	// static impls
+	public boolean isEditor() {
+		return true;
+	}
+
+	public boolean canLeaveThePage() {
+		return true;
+	}
+
+	public IManagedForm getManagedForm() {
+		// this is not a form
+		return null;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
new file mode 100644
index 0000000..ca0eb01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class TestingSection extends SigilSection {
+	
+	public TestingSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}    
+
+    protected void createSection(Section section,FormToolkit toolkit ) {
+        setTitle("Testing");
+        
+		Composite body = createTableWrapBody(1, toolkit);
+
+        toolkit.createLabel( body, "Test this project by launching a separate Newton application:" );
+        
+        Hyperlink launch = toolkit.createHyperlink( body, "Launch a newton container", SWT.NULL );
+        launch.setHref( "launchShortcut.run.org.cauldron.sigil.launching.shortcut" );
+        launch.addHyperlinkListener(this);
+        
+        Hyperlink debug = toolkit.createHyperlink( body, "Debug a newton container", SWT.NULL );
+        debug.setHref( "launchShortcut.debug.org.cauldron.sigil.launching.shortcut" );
+        debug.addHyperlinkListener(this);
+    }
+
+	public void linkActivated(HyperlinkEvent e) {
+		String href = (String) e.getHref();
+		if (href.startsWith("launchShortcut.")) { //$NON-NLS-1$
+			href = href.substring(15);
+			int index = href.indexOf('.');
+			if (index < 0)
+				return;  // error.  Format of href should be launchShortcut.<mode>.<launchShortcutId>
+			String mode = href.substring(0, index);
+			String id = href.substring(index + 1); 
+
+			//getEditor().doSave(null);
+			
+			IExtensionRegistry registry = Platform.getExtensionRegistry();
+			IConfigurationElement[] elements = registry.getConfigurationElementsFor("org.eclipse.debug.ui.launchShortcuts"); //$NON-NLS-1$
+			for (int i = 0; i < elements.length; i++) {
+				if (id.equals(elements[i].getAttribute("id"))) //$NON-NLS-1$
+					try {
+						ILaunchShortcut shortcut = (ILaunchShortcut)elements[i].createExecutableExtension("class"); //$NON-NLS-1$
+						// preLaunch();
+						shortcut.launch(new StructuredSelection(getLaunchObject()), mode);
+					} catch (CoreException e1) {
+						e1.printStackTrace();
+					}
+			}
+		}
+	}
+
+	private Object getLaunchObject() {
+		return getProjectModel().getProject();
+	}
+
+	public void linkEntered(HyperlinkEvent e) {
+	}
+
+	public void linkExited(HyperlinkEvent e) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
new file mode 100644
index 0000000..22d9824
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.actions.PruneProjectDependenciesAction;
+import org.cauldron.sigil.actions.ResolveProjectDependenciesAction;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ToolsSection extends SigilSection {
+	
+	public ToolsSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super( page, parent, project );
+	}    
+
+    protected void createSection(Section section,FormToolkit toolkit ) {
+        setTitle("Tools");
+        
+		Composite body = createTableWrapBody(1, toolkit);
+
+        toolkit.createLabel( body, "Tools to help manage this project:" );
+        
+        Hyperlink launch = toolkit.createHyperlink( body, "Resolve missing dependencies", SWT.NULL );
+        launch.setHref( "resolve" );
+        launch.addHyperlinkListener(this);
+        
+        Hyperlink debug = toolkit.createHyperlink( body, "Prune unused dependencies", SWT.NULL );
+        debug.setHref( "prune" );
+        debug.addHyperlinkListener(this);
+    }
+
+	public void linkActivated(HyperlinkEvent e) {
+		String href = (String) e.getHref();
+		if ( "resolve".equals( href ) ) {
+			handleResolve();
+		}
+		else if ( "prune".equals( href ) ) {
+			handlePrune();
+		}
+	}
+
+	private void handlePrune() {
+		new PruneProjectDependenciesAction(getProjectModel()).run();
+	}
+
+	private void handleResolve() {
+		final ISigilProjectModel project = getProjectModel();
+		new ResolveProjectDependenciesAction(project, true).run();
+	}
+
+	public void linkEntered(HyperlinkEvent e) {
+	}
+
+	public void linkExited(HyperlinkEvent e) {
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
new file mode 100644
index 0000000..7591b38
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class VersionRangeComponent extends Composite {
+	private VersionRange versions = VersionRange.ANY_VERSION;
+	
+	private Button specificButton;
+	
+	private Text specificText;
+	private Button rangeButton;
+	private Text minimumText;
+	private Text maximumText;
+	private Button minInclusiveButton;
+	private Button maxInclusiveButton;
+	
+	private Set<VersionsChangeListener> listeners = new HashSet<VersionsChangeListener>();
+	private Set<IValidationListener> validationListeners = new HashSet<IValidationListener>();
+	
+	public VersionRangeComponent(Composite parent, int style) {
+		super( parent, style );
+		createComponents(this);
+	}
+	
+	public void addVersionChangeListener(VersionsChangeListener listener) {
+		synchronized(listeners) {
+			listeners.add( listener );
+		}
+	}
+	
+	public void removeVersionChangeListener(VersionsChangeListener listener) {
+		synchronized(listeners) {
+			listeners.remove( listener );
+		}
+	}
+	
+	public void addValidationListener(IValidationListener listener) {
+		validationListeners.add(listener);
+	}
+	
+	public void removeValidationListener(IValidationListener listener) {
+		validationListeners.remove(listener);
+	}
+	
+	@Override
+	public void setEnabled(boolean enabled) {
+		super.setEnabled(enabled);
+		specificButton.setEnabled(enabled);
+		rangeButton.setEnabled(enabled);
+		if ( enabled ) {
+			specificButton.setSelection(versions.isPointVersion());
+			setSpecific();
+		}
+		else {
+			minimumText.setEnabled(enabled);
+			maximumText.setEnabled(enabled);
+			minInclusiveButton.setEnabled(enabled);
+			maxInclusiveButton.setEnabled(enabled);
+		}
+	}
+
+
+	public VersionRange getVersions() {
+		return versions;
+	}
+	
+	public void setVersions(VersionRange versions) {
+		this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+		updateFields();
+	}
+
+	private void updateFields() {
+		if ( versions.isPointVersion() ) {
+			specificButton.setSelection(true);
+			specificText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getFloor().toString() );
+		}
+		else {
+			rangeButton.setSelection( true );
+			minimumText.setText( versions.getFloor().toString() );
+			minInclusiveButton.setSelection( !versions.isOpenFloor() );
+			maximumText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getCeiling().toString() );
+			maxInclusiveButton.setSelection( !versions.isOpenCeiling() );
+		}
+		
+		setSpecific();		
+	}
+
+	private void createComponents(Composite body) {
+		setLayout( new GridLayout( 3, false ) );
+		
+		specificButton = new Button( body, SWT.RADIO );
+		specificButton.setText( "Specific:" );
+		specificButton.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				setSpecific();
+			}			
+		});
+		
+		new Label( body, SWT.NONE ).setText("Version:");
+		
+		specificText = new Text( body, SWT.BORDER );
+		specificText.addKeyListener( new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				setVersions();
+			}			
+		});
+		
+		rangeButton = new Button( body, SWT.RADIO );
+		rangeButton.setText( "Range:" );
+		
+		new Label(body, SWT.NONE).setText("Minimum:");
+		
+		minimumText = new Text( body, SWT.BORDER );
+		minimumText.addKeyListener( new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				setVersions();
+			}			
+		});
+		
+		minInclusiveButton = new Button( body, SWT.CHECK );
+		minInclusiveButton.setText( "inclusive" );
+		minInclusiveButton.setSelection(true);
+		minInclusiveButton.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				setVersions();
+			}
+		});
+		
+		new Label( body, SWT.NONE ).setText("Maximum:");
+		maximumText = new Text( body, SWT.BORDER );
+		maximumText.addKeyListener( new KeyAdapter() {
+			@Override
+			public void keyReleased(KeyEvent e) {
+				setVersions();
+			}			
+		});
+
+		maxInclusiveButton = new Button( body, SWT.CHECK );
+		maxInclusiveButton.setText( "inclusive" );
+		maxInclusiveButton.setSelection(false);
+		maxInclusiveButton.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				setVersions();
+			}
+		});
+		
+		// Layout
+		specificButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+		specificText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+		rangeButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+		minimumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		maximumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+		updateFields();
+	}
+	
+	private void setVersions() {
+		try {
+			if ( specificButton.getSelection() ) {
+				if ( "*".equals( specificText.getText() ) ) {
+					versions = VersionRange.ANY_VERSION;
+				}
+				else if ( specificText.getText().trim().length() == 0 ) {
+					versions = null;
+				}
+				else {
+					Version v = Version.parseVersion( specificText.getText().trim() );
+					versions = new VersionRange( false, v, v, false);
+				}
+			}
+			else {
+				Version min = Version.parseVersion( minimumText.getText() );
+				Version max = "*".equals( maximumText.getText() ) ? VersionRange.INFINITE_VERSION : Version.parseVersion( maximumText.getText() );
+				versions = new VersionRange( !minInclusiveButton.getSelection(), min, max, !maxInclusiveButton.getSelection() );
+			}
+			fireValidationMessage(null, IMessageProvider.NONE);
+		}
+		catch (IllegalArgumentException e) {
+			versions = null;
+			fireValidationMessage("Invalid version", IMessageProvider.ERROR);
+		}
+
+		fireVersionChange();
+	}
+	
+	private void fireVersionChange() {
+		synchronized( listeners ) {
+			for ( VersionsChangeListener l : listeners ) {
+				l.versionsChanged(versions);
+			}
+		}
+	}
+
+	private void fireValidationMessage(String message, int level) {
+		for (IValidationListener validationListener : validationListeners) {
+			validationListener.validationMessage(message, level);
+		}
+	}
+
+	private void setSpecific() {
+		boolean specific = specificButton.getSelection();
+		specificButton.setSelection(specific);
+		specificText.setEnabled(specific);
+		minimumText.setEnabled(!specific);
+		maximumText.setEnabled(!specific);
+		minInclusiveButton.setEnabled(!specific);
+		maxInclusiveButton.setEnabled(!specific);
+		setVersions();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
new file mode 100644
index 0000000..d97bdc2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface VersionsChangeListener {
+	void versionsChanged(VersionRange range);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
new file mode 100644
index 0000000..6f0e397
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+public class WrappedContentProposal<T> implements IContentProposal {
+	
+	private final T element;
+	private final IElementDescriptor<? super T> descriptor;
+
+	private WrappedContentProposal(T element, IElementDescriptor<? super T> descriptor) {
+		this.element = element;
+		this.descriptor = descriptor;
+	}
+	
+	public static <T> WrappedContentProposal<T> newInstance(T element, IElementDescriptor<? super T> descriptor) {
+		return new WrappedContentProposal<T>(element, descriptor);
+	}
+
+	public String getContent() {
+		return descriptor.getName(element);
+	}
+
+	public int getCursorPosition() {
+		return 0;
+	}
+
+	public String getDescription() {
+		return null;
+	}
+
+	public String getLabel() {
+		return descriptor.getLabel(element);
+	}
+
+	public T getElement() {
+		return element;
+	}
+
+	@Override
+	public String toString() {
+		return getLabel();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
new file mode 100644
index 0000000..34c8bb6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface IFormValueConverter {
+	String getLabel(Object value);
+	Object getValue(String label);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
new file mode 100644
index 0000000..89afefa
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface ISigilFormEntryListener {
+	void browseButtonSelected(SigilFormEntry form);
+	void textValueChanged(SigilFormEntry form);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
new file mode 100644
index 0000000..ea5bf9d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class SigilFormEntry {
+	private static final IFormValueConverter NULL_DESCRIPTOR = new IFormValueConverter() {
+		public String getLabel(Object value) {
+			return (String) value;
+		}
+
+		public Object getValue(String label) {
+			return label;
+		}
+	};
+	
+	private Label lbl;
+	private Text txt;
+	private Button btn;
+	private IFormValueConverter descriptor;
+	private boolean freeText = true;
+	
+	private Object value;
+	private ISigilFormEntryListener listener;
+	
+	public SigilFormEntry(Composite parent, FormToolkit toolkit, String title) {
+		this(parent, toolkit, title, null, null);
+	}
+	
+	public SigilFormEntry(Composite parent, FormToolkit toolkit, String title, String browse, IFormValueConverter descriptor) {
+		this.descriptor = descriptor == null ? NULL_DESCRIPTOR : descriptor;
+		createComponent(parent, title, browse, toolkit);
+	}
+	
+	public void setFormEntryListener(ISigilFormEntryListener listener) {
+		this.listener = listener;
+	}
+	
+	public void setValue(Object value) {
+		this.value = value;
+		String text = descriptor.getLabel(value);
+		if ( text == null ) {
+			text = "";
+		}
+		txt.setText(text);
+		handleValueChanged();
+	}
+	
+	public Object getValue() {
+		return value;
+	}
+
+	public void setFreeText(boolean freeText) {
+		this.freeText = freeText;
+	}
+	
+	public void setEditable(boolean editable) {
+		lbl.setEnabled(editable);
+		txt.setEditable(editable);
+		if ( btn != null ) {
+			btn.setEnabled(editable);
+		}
+	}	
+		
+	private void createComponent(Composite parent, String title, String browse, FormToolkit toolkit) {
+		lbl = toolkit.createLabel(parent, title);
+		lbl.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+		
+		txt = toolkit.createText(parent, "", SWT.SINGLE | SWT.BORDER);
+		txt.addKeyListener( new KeyAdapter() {
+			@Override
+			public void keyPressed(KeyEvent e) {
+				if ( freeText ) {
+					switch ( e.character ) {
+					case '\r': handleValueChanged(); 
+					}
+				}
+				else {
+					switch ( e.character ) {
+					case '\b': 
+						setValue(null); 
+						handleValueChanged(); 
+					default:
+						e.doit = false;
+						break;
+					}
+				}
+			}
+		});
+		txt.addFocusListener( new FocusAdapter() {
+			@Override
+			public void focusLost(FocusEvent e) {
+				handleValueChanged();
+			}
+		});
+		
+		if ( browse != null ) {
+			btn = toolkit.createButton(parent, browse, SWT.PUSH);
+			btn.addSelectionListener( new SelectionAdapter() {
+				@Override
+				public void widgetSelected(SelectionEvent e) {
+					handleBrowseSelected();
+				}
+			});
+		}
+		
+		fillIntoGrid(parent);
+	}
+
+	private void handleBrowseSelected() {
+		if ( listener != null ) {
+			listener.browseButtonSelected(this);
+		}
+	}
+
+	private void handleValueChanged() {
+		if ( freeText ) {
+			this.value = descriptor.getValue(txt.getText());
+		}
+		if ( listener != null ) {
+			listener.textValueChanged(this);
+		}
+	}
+
+	private void fillIntoGrid(Composite parent) {
+		if ( parent.getLayout() instanceof GridLayout ) {
+			GridLayout layout = (GridLayout) parent.getLayout();
+			
+			int cols = layout.numColumns;
+			
+			lbl.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, false, false) );
+			
+			if ( btn == null ) {
+				txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 1), 1 ) );
+			}
+			else {
+				txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 2), 1 ) );
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
new file mode 100644
index 0000000..3170d01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public class SigilFormEntryAdapter implements ISigilFormEntryListener{
+	public void browseButtonSelected(SigilFormEntry form) {
+	}
+
+	public void textValueChanged(SigilFormEntry form) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
new file mode 100644
index 0000000..a2873ad
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+
+public class SigilPage extends FormPage {
+
+	public SigilPage(FormEditor editor, String id, String title) {
+		super(editor, id, title);
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
new file mode 100644
index 0000000..b825ec0
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.pde.internal.ui.editor.FormLayoutFactory;
+import org.eclipse.pde.internal.ui.parts.FormEntry;
+import org.eclipse.pde.internal.ui.parts.IFormEntryListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IPartSelectionListener;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+@SuppressWarnings("restriction")
+public abstract class SigilSection extends SectionPart implements IFormEntryListener, IPartSelectionListener {
+
+	private SigilPage page;
+	private ISigilProjectModel project;
+	
+	public SigilSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+		super(parent, page.getManagedForm().getToolkit(), ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED );
+		this.project = project;
+		this.page = page;
+		createSection( getSection(), page.getManagedForm().getToolkit() );		
+	}
+
+	public ISigilProjectModel getProjectModel() {
+		return project;
+	}
+	
+	public SigilPage getPage() {
+		return page;
+	}
+	
+	public void setExpanded( boolean expanded ) {
+		getSection().setExpanded(expanded);
+	}
+	
+	protected abstract void createSection(Section section,FormToolkit toolkit ) throws CoreException;
+	
+	protected void setTitle( String title ) {
+			Section section = getSection();
+	        section.setText( title );
+			section.setLayout(FormLayoutFactory.createClearTableWrapLayout(false, 1));
+			TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+			section.setLayoutData(data);				
+	}
+	
+	protected void setMarker(String type, String message, int priority, int severity) throws CoreException {
+		IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+		IMarker marker = file.getFile().createMarker(type);
+		marker.setAttribute( IMarker.MESSAGE, message );
+		marker.setAttribute(IMarker.PRIORITY, priority);
+		marker.setAttribute( IMarker.SEVERITY, severity );
+	}
+	
+	protected void clearMarkers() throws CoreException {
+		IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+		file.getFile().deleteMarkers(null, true, IResource.DEPTH_INFINITE );
+	}
+	
+	protected Composite createTableWrapBody( int columns, FormToolkit toolkit ) {
+		Section section = getSection();
+        Composite client = toolkit.createComposite(section);
+        
+        TableWrapLayout layout = new TableWrapLayout();
+        layout.leftMargin = layout.rightMargin = toolkit.getBorderStyle() != SWT.NULL ? 0 : 2;
+        layout.numColumns = columns;
+        client.setLayout(layout);
+        client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+		
+        section.setClient(client);
+        
+        return client;
+	}
+	
+	protected Composite createGridBody( int columns, boolean columnsSameWidth, FormToolkit toolkit ) {
+		Section section = getSection();
+        Composite client = toolkit.createComposite(section);
+        
+		GridLayout layout = new GridLayout();
+		
+		layout.makeColumnsEqualWidth = columnsSameWidth;
+		layout.numColumns = columns;
+        client.setLayout(layout);
+        
+        client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+		
+        section.setClient(client);
+        
+        return client;
+	}
+	
+	public void browseButtonSelected(FormEntry entry) {
+	}
+
+	public void focusGained(FormEntry entry) {
+	}
+
+	public void selectionChanged(FormEntry entry) {
+	}
+
+	public void textDirty(FormEntry entry) {
+	}
+
+	public void textValueChanged(FormEntry entry) {
+	}
+
+	public void linkActivated(HyperlinkEvent e) {
+	}
+
+	public void linkEntered(HyperlinkEvent e) {
+	}
+
+	public void linkExited(HyperlinkEvent e) {
+	}
+
+	public void selectionChanged(IFormPart part, ISelection selection) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
new file mode 100644
index 0000000..df5314b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class FileSystemRepositoryWizard extends RepositoryWizard {
+	@Override
+	public void addPages() {
+		addPage( new FileSystemRepositoryWizardPage(this) );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
new file mode 100644
index 0000000..a46f7d9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.io.File;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+
+public class FileSystemRepositoryWizardPage extends RepositoryWizardPage
+		implements IWizardPage {
+
+	private DirectoryFieldEditor dirEditor;
+
+	protected FileSystemRepositoryWizardPage(RepositoryWizard parent) {
+		super("File System Repository", parent);
+	}
+
+	@Override
+	public void createFieldEditors() {
+		dirEditor = new DirectoryFieldEditor("dir", "Directory:", getFieldEditorParent() );
+		dirEditor.getTextControl( getFieldEditorParent() ).addModifyListener( new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				checkPageComplete();
+			} 
+		} );
+		
+		addField( dirEditor );
+		addField(new BooleanFieldEditor("recurse", "Recurse:", getFieldEditorParent() ) );
+	}
+
+	@Override
+	protected void checkPageComplete() {
+		super.checkPageComplete();
+		if ( isPageComplete() ) {
+			setPageComplete(dirEditor.getStringValue().length() > 0);
+			if ( isPageComplete() ) {
+				if ( new File( dirEditor.getStringValue()).isDirectory() ) {
+					setPageComplete(true);
+					setErrorMessage(null);
+				}
+				else {
+					setPageComplete(false);
+					setErrorMessage("Invalid directory");
+				}
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
new file mode 100644
index 0000000..ae8da79
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class OSGiInstallRepositoryWizard extends RepositoryWizard {
+	@Override
+	public void addPages() {
+		addPage( new OSGiInstallRepositoryWizardPage(this) );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
new file mode 100644
index 0000000..772b151
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+
+public class OSGiInstallRepositoryWizardPage extends RepositoryWizardPage {
+
+	protected OSGiInstallRepositoryWizardPage(RepositoryWizard parent) {
+		super("OSGi Install Repository", parent);
+	}
+
+	@Override
+	public void createFieldEditors() {
+		ArrayList<String[]> installs = new ArrayList<String[]>();
+		for ( String id : SigilCore.getInstallManager().getInstallIDs() ) {
+			IOSGiInstall i = SigilCore.getInstallManager().findInstall(id);
+			installs.add( new String[] { i.getType().getName(), id } );
+		}
+		String[][] strs = installs.toArray( new String[installs.size()][] );
+		
+		RadioGroupFieldEditor editor = new RadioGroupFieldEditor(
+				"id", 
+				"Install", 
+				1, strs, getFieldEditorParent() );
+		
+		addField(editor);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
new file mode 100644
index 0000000..34d428c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.perspective;
+
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.progress.IProgressConstants;
+
+public class SigilPerspectiveFactory implements IPerspectiveFactory {
+	
+	private static final String ID_PROJECT_EXPLORER= "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
+	private static final String ID_SEARCH_VIEW = "org.eclipse.search.ui.views.SearchView"; //$NON-NLS-1$
+	private static final String ID_CONSOLE_VIEW = "org.eclipse.ui.console.ConsoleView"; //$NON-NLS-1$
+	private static final String ID_TEMPLATES_VIEW = "org.eclipse.ui.texteditor.TemplatesView"; //$NON-NLS-N$
+	
+
+	public void createInitialLayout(IPageLayout layout) {
+		/*
+		 * Use ProjectExplorer vs PackageExplorer due to a bug with Drag and Drop on Mac OS X which affects PackageExplorer
+		 * but not ProjectExplorer. https://bugs.eclipse.org/bugs/show_bug.cgi?id=243529
+		 */
+ 		String editorArea = layout.getEditorArea();
+		
+		IFolderLayout folder= layout.createFolder("left", IPageLayout.LEFT, (float)0.25, editorArea); //$NON-NLS-1$
+		folder.addView(ID_PROJECT_EXPLORER);
+		folder.addView(JavaUI.ID_TYPE_HIERARCHY);
+		folder.addPlaceholder(IPageLayout.ID_RES_NAV);
+		folder.addPlaceholder(SigilUI.ID_REPOSITORY_VIEW);
+		folder.addView(IPageLayout.ID_OUTLINE);		
+		
+		IFolderLayout outputfolder= layout.createFolder("bottom", IPageLayout.BOTTOM, (float)0.75, editorArea); //$NON-NLS-1$
+		outputfolder.addView(IPageLayout.ID_PROBLEM_VIEW);
+		outputfolder.addView(JavaUI.ID_JAVADOC_VIEW);
+		outputfolder.addView(JavaUI.ID_SOURCE_VIEW);
+		outputfolder.addPlaceholder(ID_SEARCH_VIEW);
+		outputfolder.addPlaceholder(ID_CONSOLE_VIEW);
+		outputfolder.addPlaceholder(IPageLayout.ID_BOOKMARKS);
+		outputfolder.addPlaceholder(IProgressConstants.PROGRESS_VIEW_ID);
+		outputfolder.addPlaceholder(SigilUI.ID_DEPENDENCY_VIEW);
+		outputfolder.addPlaceholder(SigilUI.ID_INSTANCES_VIEW);
+		
+		layout.addActionSet(IDebugUIConstants.LAUNCH_ACTION_SET);
+		layout.addActionSet(JavaUI.ID_ACTION_SET);
+		layout.addActionSet(JavaUI.ID_ELEMENT_CREATION_ACTION_SET);
+		layout.addActionSet(IPageLayout.ID_NAVIGATE_ACTION_SET);
+		
+		// views - sigil
+		layout.addShowViewShortcut(SigilUI.ID_REPOSITORY_VIEW);
+		layout.addShowViewShortcut(SigilUI.ID_DEPENDENCY_VIEW);
+		layout.addShowViewShortcut(SigilUI.ID_INSTANCES_VIEW);
+		
+		// views - java
+		layout.addShowViewShortcut(JavaUI.ID_PACKAGES);
+		layout.addShowViewShortcut(JavaUI.ID_TYPE_HIERARCHY);
+		layout.addShowViewShortcut(JavaUI.ID_SOURCE_VIEW);
+		layout.addShowViewShortcut(JavaUI.ID_JAVADOC_VIEW);
+
+		// views - search
+		layout.addShowViewShortcut(ID_SEARCH_VIEW);
+		
+		// views - debugging
+		layout.addShowViewShortcut(ID_CONSOLE_VIEW);
+
+		// views - standard workbench
+		layout.addShowViewShortcut(IPageLayout.ID_OUTLINE);
+		layout.addShowViewShortcut(IPageLayout.ID_PROBLEM_VIEW);
+		layout.addShowViewShortcut(IPageLayout.ID_RES_NAV);
+		layout.addShowViewShortcut(IPageLayout.ID_TASK_LIST);
+		layout.addShowViewShortcut(IProgressConstants.PROGRESS_VIEW_ID);
+		layout.addShowViewShortcut(ID_PROJECT_EXPLORER);
+				
+		// new actions - Java project creation wizard
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewPackageCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewClassCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewEnumCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard");	 //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard"); //$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.folder");//$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.file");//$NON-NLS-1$
+		layout.addNewWizardShortcut("org.eclipse.ui.editors.wizards.UntitledTextFileWizard");//$NON-NLS-1$
+		
+		layout.addNewWizardShortcut("org.cauldron.sigil.editors.newCompositeWizard");
+		layout.addNewWizardShortcut("org.cauldron.sigil.editors.newSystemWizard");
+		layout.addNewWizardShortcut("org.cauldron.sigil.editors.newProjectWizard");
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
new file mode 100644
index 0000000..134f3c3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.eclipse.core.internal.preferences.PrefsMessages;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class ExcludedResourcesPrefsPage extends PreferencePage implements IWorkbenchPreferencePage {
+	
+	private TableViewer viewer;
+	private IWorkbench workbench;
+	private ArrayList<String> resources;
+
+	public ExcludedResourcesPrefsPage() {
+		super();
+		setDescription("Specify resources that should not be offered for inclusion in a generated bundle.");
+	}
+
+	@Override
+	protected Control createContents(Composite parent) {
+		// Create controls
+		Composite composite = new Composite(parent, SWT.NONE);
+		Table table = new Table(composite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
+		
+		Button btnAdd = new Button(composite, SWT.PUSH);
+		btnAdd.setText("Add");
+		
+		final Button btnRemove = new Button(composite, SWT.PUSH);
+		btnRemove.setText("Remove");
+		btnRemove.setEnabled(false);
+		
+		// Create viewer
+		viewer = new TableViewer(table);
+		viewer.setContentProvider(new ArrayContentProvider());
+		
+		// Load data
+		loadPreferences(false);
+		viewer.setInput(resources);
+		
+		// Listeners
+		viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				btnRemove.setEnabled(!viewer.getSelection().isEmpty());
+			}
+		});
+		
+		btnRemove.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+				Object[] deleted = selection.toArray();
+				for(Object delete : deleted) {
+					resources.remove(delete);
+				}
+				viewer.remove(deleted);
+			}
+		});
+		
+		btnAdd.addSelectionListener(new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				InputDialog dialog = new InputDialog(getShell(), "Add Resource", "Enter resource name", "", new IInputValidator() {
+					public String isValid(String newText) {
+						String error = null;
+						if(newText == null || newText.length() == 0) {
+							error = "Name must not be empty.";
+						} else if(resources.contains(newText)) {
+							error = "Specified resource name is already on the list.";
+						}
+						return error;
+					}
+				});
+				
+				if(dialog.open() == Window.OK) {
+					String value = dialog.getValue();
+					resources.add(value);
+					viewer.add(value);
+				}
+			}
+		});
+		
+		// Layout
+		composite.setLayout(new GridLayout(2, false));
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+		btnAdd.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		btnRemove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		
+		return composite;
+	}
+
+	private void loadPreferences(boolean useDefaults) {
+		String resourcesListStr = useDefaults
+				? getPreferenceStore().getDefaultString(SigilCore.DEFAULT_EXCLUDED_RESOURCES)
+				: getPreferenceStore().getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES);
+		String[] resourcesArray = PrefsUtils.stringToArray(resourcesListStr);
+		
+		resources = new ArrayList<String>(resourcesArray.length);
+		for (String resource : resourcesArray) {
+			resources.add(resource);
+		}
+	}
+
+	public void init(IWorkbench workbench) {
+		this.workbench = workbench;
+	}
+	
+	@Override
+	protected IPreferenceStore doGetPreferenceStore() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+	
+	@Override
+	public boolean performOk() {
+		String resourcesStr = PrefsUtils.arrayToString(resources.toArray(new String[resources.size()]));
+		getPreferenceStore().setValue(SigilCore.DEFAULT_EXCLUDED_RESOURCES, resourcesStr);
+		return true;
+	}
+	
+	@Override
+	protected void performDefaults() {
+		super.performDefaults();
+		loadPreferences(true);
+		viewer.setInput(resources);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
new file mode 100644
index 0000000..d7b6786
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class LibraryConfigurationDialog extends TitleAreaDialog {
+
+	private static final Comparator<IPackageImport> COMPARATOR = new Comparator<IPackageImport>() {
+		public int compare(IPackageImport o1, IPackageImport o2) {
+			return o1.getPackageName().compareTo(o2.getPackageName());
+		}
+	};
+	
+	private String name;
+	private Version version;
+	private TreeSet<IPackageImport> packageImports = new TreeSet<IPackageImport>(COMPARATOR);
+	
+	private boolean editOnly;
+
+	private TableViewer viewer;
+	private Text txtName;
+	private Text txtVersion;
+	
+	public LibraryConfigurationDialog(Shell parentShell) {
+		super(parentShell);
+		name = "";
+		version = Version.emptyVersion;
+	}
+
+	public LibraryConfigurationDialog(Shell parentShell, ILibrary lib) {
+		super(parentShell);
+		editOnly = true;
+		name = lib.getName();
+		version = lib.getVersion();
+		packageImports.addAll(  lib.getImports() );
+	}
+	
+	@Override
+	protected Control createDialogArea(Composite par) {
+		setTitle("Add Library");
+		Composite container = (Composite) super.createDialogArea(par);
+		
+		Composite topPanel = new Composite(container, SWT.NONE);
+				
+		new Label(topPanel, SWT.NONE).setText("Name");
+		
+		txtName = new Text(topPanel, SWT.BORDER);
+		txtName.setEditable( !editOnly );
+		if(name != null) txtName.setText(name);
+		
+		new Label(topPanel, SWT.NONE).setText("Version");
+		
+		txtVersion = new Text(topPanel, SWT.BORDER);
+		txtVersion.setText( version.toString() );
+		txtVersion.setEditable( !editOnly );
+		
+		Composite bottomPanel = new Composite(container, SWT.NONE);
+		
+		Table table = new Table(bottomPanel, SWT.BORDER);
+		table.setSize( new Point( 300, 200 ) );
+		
+		Button add = new Button(bottomPanel, SWT.PUSH);
+		add.setText("Add...");
+		
+		final Button edit = new Button(bottomPanel, SWT.PUSH);
+		edit.setText("Edit...");
+		edit.setEnabled(false);
+		
+		final Button remove = new Button(bottomPanel, SWT.PUSH);
+		remove.setText("Remove");
+		remove.setEnabled(false);
+		
+		updateState();
+		
+		// Hookup Listeners
+		txtName.addModifyListener(new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				updateState();
+			}
+		});
+		txtVersion.addModifyListener(new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				updateState();
+			}
+		});
+		add.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleAdd();
+			}
+		});
+		edit.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleEdit();
+			}
+		});
+		remove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleRemove();
+			}
+		});
+		
+		// Layout
+		topPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		topPanel.setLayout(new GridLayout(2, false));
+		txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		
+		bottomPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		bottomPanel.setLayout(new GridLayout(2, false));
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+		add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		
+		// Table Viewer
+		viewer = new TableViewer(table);
+		viewer.setLabelProvider(new LabelProvider() {
+			@Override
+			public String getText(Object element) {
+				IPackageImport pi = (IPackageImport) element;
+				return pi.getPackageName() + " " + pi.getVersions();
+			}			
+		});
+		viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				edit.setEnabled(!event.getSelection().isEmpty());
+				remove.setEnabled(!event.getSelection().isEmpty());
+			}
+		});
+		viewer.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		} );
+
+		viewer.setInput(packageImports);
+		return container;
+	}
+	
+	private void updateState() {
+		String error = null;
+		String warning = null;
+		
+		name = txtName.getText();
+		
+		try {
+			version = new Version(txtVersion.getText());
+			if(version.getQualifier().indexOf('_') > -1) {
+				warning = "The use of underscores in a version qualifier is discouraged.";
+			}
+		} catch (IllegalArgumentException e) {
+			version = null;
+			error = "Invalid version format";
+		}
+		
+		Button okButton = getButton(IDialogConstants.OK_ID);
+		if(okButton != null && !okButton.isDisposed()) okButton.setEnabled(allowOkay());
+		
+		setErrorMessage(error);
+		setMessage(warning, IMessageProvider.WARNING);
+	}
+	
+	private boolean allowOkay() {
+		return name != null && name.length() > 0 && version != null;
+	}
+	
+	@Override
+	protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
+		Button button = super.createButton(parent, id, label, defaultButton);
+		if(id == IDialogConstants.OK_ID) {
+			button.setEnabled(allowOkay());
+		}
+		return button;
+	}
+	
+	private void handleAdd() {
+		/*NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog(getShell(), "Add Imported Package", null, packageImports);
+		if ( dialog.open() == Window.OK ) {
+			IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+			pi.setPackageName(dialog.getSelectedName());
+			pi.setVersions(dialog.getSelectedVersions());
+			pi.setOptional(dialog.isOptional());
+			
+			packageImports.add(pi);
+			viewer.refresh();
+		}*/
+	}
+
+	private void handleEdit() {
+		/*IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+		
+		boolean changed = false;
+		
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {	
+				IPackageImport packageImport = i.next();
+				NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog( getShell(), "Edit Imported Package", packageImport, packageImports );
+				dialog.setVersions( packageImport.getVersions() );
+				dialog.setOptional(packageImport.isOptional());
+				if ( dialog.open() == Window.OK ) {
+					changed = true;
+					String packageName = dialog.getSelectedName();
+					VersionRange versionRange = dialog.getSelectedVersions();
+					
+					IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+					newImport.setPackageName(packageName);
+					newImport.setVersions(versionRange);
+					newImport.setOptional(dialog.isOptional());
+					
+					packageImports.remove(packageImport);
+					packageImports.add(newImport);
+				}
+			}
+		}
+		
+		if ( changed ) {
+			viewer.refresh();
+		} */
+	}
+
+	private void handleRemove() {
+		IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+		if ( !selection.isEmpty() ) {
+			for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {			
+				packageImports.remove( i.next() );
+			}		
+			
+			viewer.refresh();
+		}
+	}
+
+	public ILibrary getLibrary() {
+		ILibrary library = ModelElementFactory.getInstance().newModelElement(ILibrary.class);
+		
+		library.setName(name);
+		library.setVersion(version);
+		
+		for (IPackageImport pi : packageImports) {
+			library.addImport(pi);
+		}
+		
+		return library;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
new file mode 100644
index 0000000..cf77287
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;

+

+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.IPreferenceStore;

+import org.eclipse.jface.preference.PreferencePage;

+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;

+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;

+import org.eclipse.swt.layout.GridLayout;

+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;

+import org.eclipse.swt.widgets.Control;

+import org.eclipse.swt.widgets.Label;

+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;

+import org.eclipse.ui.IWorkbenchPreferencePage;

+

+public class LibraryPreferencePage extends PreferencePage implements

+		IWorkbenchPreferencePage {

+

+	private TreeSet<ILibrary> libraries;
+	private TableViewer libraryView;
+	
+	private Table table;
+	private Button btnAdd;
+	private Button btnEdit;
+	private Button btnRemove;
+	
+	public void init(IWorkbench workbench) {

+	}

+	

+	@Override

+	protected Control createContents(Composite parent) {

+		Control control = initContents( parent );

+		loadPreferences();

+		return control;

+	}

+

+	@Override

+	protected IPreferenceStore doGetPreferenceStore() {

+		return SigilCore.getDefault().getPreferenceStore();

+	}

+

+	@Override

+	protected void performDefaults() {

+		super.performDefaults();

+	}

+

+	@Override

+	public boolean performOk() {

+		IPreferenceStore prefs = getPreferenceStore();
+		for ( String key : prefs.getString( SigilCore.LIBRARY_KEYS_PREF ).split( "," ) ) {
+			prefs.setToDefault(key);
+		}
+		
+		StringBuffer keys = new StringBuffer();
+		
+		for ( ILibrary lib : libraries ) {
+			throw new IllegalStateException( "XXX-FIXME-XXX");
+		}
+		
+		prefs.setValue( SigilCore.LIBRARY_KEYS_PREF, keys.toString() );
+		
+		return true;
+	}

+	

+	private Control initContents(Composite parent) {

+		Composite control = new Composite(parent, SWT.NONE);

+		control.setFont(parent.getFont());
+		

+		GridLayout grid = new GridLayout(3, false);

+		control.setLayout(grid);

+		

+		initRepositories(control);
+		
+		return control;

+	}
+	
+	private void initRepositories(Composite composite) {
+		// Create controls
+		new Label(composite, SWT.NONE).setText("Libraries:");
+		new Label(composite, SWT.NONE); // Spacer
+		table = new Table(composite, SWT.SINGLE | SWT.BORDER );
+		//table.setFont(control.getFont());
+		btnAdd = new Button(composite, SWT.PUSH);
+		btnAdd.setText("Add...");
+		//add.setFont(control.getFont());
+		btnEdit = new Button(composite, SWT.PUSH);
+		btnEdit.setText("Edit...");
+		//edit.setFont(control.getFont());
+		btnRemove = new Button(composite, SWT.PUSH);
+		btnRemove.setText("Remove");
+		//remove.setFont(control.getFont());
+		
+		// Table Model
+		libraries = new TreeSet<ILibrary>(new Comparator<ILibrary> () {
+			public int compare(ILibrary l1, ILibrary l2) {
+				int c = l1.getName().compareTo( l2.getName() );
+				if ( c == 0 ) {
+					c = l1.getVersion().compareTo( l2.getVersion() );
+				}
+				return c;
+			}
+		});
+		libraryView = new TableViewer(table);
+		libraryView.setLabelProvider(new LabelProvider() {
+			public String getText(Object element) {
+				ILibrary rep = (ILibrary) element;
+				return rep.getName() + " " + rep.getVersion();
+			}			
+		});
+		libraryView.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		} );
+		libraryView.setInput(libraries);
+		
+		// Initialize controls
+		updateButtonStates();
+		
+		// Hookup Listeners
+		libraryView.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				updateButtonStates();
+			}
+		});
+		btnAdd.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleAdd();
+			}
+		});
+		btnEdit.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleEdit();
+			}
+		});
+		btnRemove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				handleRemove();
+			}
+		});
+		
+		// Layout
+		composite.setLayout(new GridLayout(2, false));
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+		GridDataFactory buttonGD = GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER);
+		btnAdd.setLayoutData(buttonGD.create());
+		btnEdit.setLayoutData(buttonGD.create());
+		btnRemove.setLayoutData(buttonGD.create());
+	}
+	
+	private void updateButtonStates() {
+		ISelection sel = libraryView.getSelection();
+		btnEdit.setEnabled(!sel.isEmpty());
+		btnRemove.setEnabled(!sel.isEmpty());
+	}
+	
+
+	private void handleAdd() {
+		LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell());
+		if ( d.open() == Window.OK ) {
+			libraries.add(d.getLibrary());
+			libraryView.refresh();
+		}
+	}
+
+	private void handleEdit() {
+		IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+		boolean change = false;
+		
+		
+		for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+			ILibrary lib = i.next();
+			LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell(), lib );
+			if ( d.open() == Window.OK ) {
+				libraries.remove(lib);
+				libraries.add(d.getLibrary());
+				change = true;
+			}
+		}
+		
+		if ( change ) {
+			libraryView.refresh();
+		}
+	}
+
+	private void handleRemove() {
+		IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+		for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+			libraries.remove(i);
+		}
+		libraryView.refresh();
+	}
+
+	private void loadPreferences() {

+		IPreferenceStore prefs = getPreferenceStore();
+		String keys = prefs.getString( SigilCore.LIBRARY_KEYS_PREF );
+		if ( keys.trim().length() > 0 ) {
+			for ( String key : keys.split( "," ) ) {
+				String libStr = prefs.getString(key);
+				// XXX-FIXME-XXX parse library string
+				// lib = parse(libstr);
+				// libraries.add(lib);
+			}
+			libraryView.refresh();
+		}
+	}

+}

diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
new file mode 100644
index 0000000..45ace9f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Shell;
+
+public class OptionalPrompt {
+	public static boolean optionallyPrompt(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+		boolean result = false;
+		
+		PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+		switch(value) {
+		case Always:
+			result = true;
+			break;
+		case Never:
+			result = false;
+			break;
+		case Prompt:
+			MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+			result = (dialog.getReturnCode() == IDialogConstants.YES_ID);
+			if(dialog.getToggleState()) {
+				// User said don't ask again... take the current answer as the new preference
+				prefStore.setValue(prefName, result ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+			}
+		}
+		
+		return result;
+	}
+	
+	public static int optionallyPromptWithCancel(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+		int result = IDialogConstants.NO_ID;
+		
+		PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+		switch(value) {
+		case Always:
+			result = IDialogConstants.YES_ID;
+			break;
+		case Never:
+			result = IDialogConstants.NO_ID;
+			break;
+		case Prompt:
+			MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+			result = dialog.getReturnCode();
+			if ( result != IDialogConstants.CANCEL_ID ) {
+				if(dialog.getToggleState()) {
+					// User said don't ask again... take the current answer as the new preference
+					prefStore.setValue(prefName, (result == IDialogConstants.YES_ID) ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+				}
+			}
+		}
+		
+		return result;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
new file mode 100644
index 0000000..07a564c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.jface.preference.PreferencePage;
+
+public abstract class ProjectDependentPreferencesPage extends PreferencePage {
+
+	public ProjectDependentPreferencesPage(String title) {
+		super(title);
+	}
+
+	@Override
+	public boolean performOk() {
+		if ( isDirty() ) {
+			return ProjectUtils.runTaskWithRebuildCheck(new Runnable() {
+				public void run() {
+					doSave();
+				}
+				
+			}, getShell());
+		}
+		else {
+			return true;
+		}
+	}
+
+	protected abstract void doSave();
+
+	protected abstract boolean isDirty();
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
new file mode 100644
index 0000000..4a16093
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class SigilPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+	@Override
+	protected void createFieldEditors() {
+		RadioGroupFieldEditor impExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Imports for New Exports", 1, new String[][] {
+				new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+				new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+				new String[] { "Never", PromptablePreference.Never.toString() }
+		}, getFieldEditorParent(), true);
+		
+		addField(impExpField);
+		
+		RadioGroupFieldEditor rebuildExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild Projects On Install Change", 1, new String[][] {
+				new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+				new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+				new String[] { "Never", PromptablePreference.Never.toString() }
+		}, getFieldEditorParent(), true);
+		
+		addField(rebuildExpField);		
+	}
+	
+	@Override
+	protected IPreferenceStore doGetPreferenceStore() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+
+	public void init(IWorkbench workbench) {
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
new file mode 100644
index 0000000..82cd79c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.osgi.framework.Version;
+
+public class VersionsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+
+	private static final Version SAMPLE_VERSION = new Version(1, 2, 3, "qualifier");
+
+	private IWorkbench workbench;
+
+	private Button btnLowerBoundExact;
+	private Button btnLowerBoundMicro;
+	private Button btnLowerBoundMinor;
+	private Button btnLowerBoundMajor;
+	private Button btnLowerBoundAny;
+
+	private Button btnUpperBoundExact;
+	private Button btnUpperBoundMicro;
+	private Button btnUpperBoundMinor;
+	private Button btnUpperBoundMajor;
+	private Button btnUpperBoundAny;
+
+	private Text txtSampleVersion;
+	private Label lblCalculatedRange;
+	private Text txtMatchVersion;
+	private Label lblMatchResult;
+	
+	private VersionRangeBoundingRule lowerBoundRule;
+	private VersionRangeBoundingRule upperBoundRule;
+
+	private Version sampleVersion = SAMPLE_VERSION;
+	private Version matchVersion = null;
+	
+	public VersionsPreferencePage() {
+		super();
+		setDescription("Specify the Lower and Upper bounds for a default version range calculated from a point version, e.g. \"1.2.3.qualifier\"");
+	}
+	
+	@Override
+	protected Control createContents(Composite parent) {
+		// Create controls
+		Composite composite = new Composite(parent, SWT.NONE);
+
+		Group grpLowerBound = new Group(composite, SWT.NONE);
+		grpLowerBound.setText("Lower Bound");
+		btnLowerBoundExact = new Button(grpLowerBound, SWT.RADIO);
+		btnLowerBoundExact.setText("Exact e.g. [1.2.3.qualifer, ...)");
+		btnLowerBoundMicro = new Button(grpLowerBound, SWT.RADIO);
+		btnLowerBoundMicro.setText("Micro e.g. [1.2.3, ...)");
+		btnLowerBoundMinor = new Button(grpLowerBound, SWT.RADIO);
+		btnLowerBoundMinor.setText("Minor e.g. [1.2, ...)");
+		btnLowerBoundMajor = new Button(grpLowerBound, SWT.RADIO);
+		btnLowerBoundMajor.setText("Major e.g. [1, ...)");
+		btnLowerBoundAny = new Button(grpLowerBound, SWT.RADIO);
+		btnLowerBoundAny.setText("Any e.g. [0, ...)");
+
+		Group grpUpperBound = new Group(composite, SWT.NONE);
+		grpUpperBound.setText("Upper Bound");
+
+		btnUpperBoundExact = new Button(grpUpperBound, SWT.RADIO);
+		btnUpperBoundExact.setText("Exact e.g. [..., 1.2.3.qualifer]");
+		btnUpperBoundMicro = new Button(grpUpperBound, SWT.RADIO);
+		btnUpperBoundMicro.setText("Micro e.g. [..., 1.2.4)");
+		btnUpperBoundMinor = new Button(grpUpperBound, SWT.RADIO);
+		btnUpperBoundMinor.setText("Minor e.g. [..., 1.3)");
+		btnUpperBoundMajor = new Button(grpUpperBound, SWT.RADIO);
+		btnUpperBoundMajor.setText("Major e.g. [..., 2)");
+		btnUpperBoundAny = new Button(grpUpperBound, SWT.RADIO);
+		btnUpperBoundAny.setText("Any e.g. [..., \u221e)");
+		
+		Group grpRangeTest = new Group(composite, SWT.NONE);
+		grpRangeTest.setText("Range Test");
+		new Label(grpRangeTest, SWT.NONE).setText("Sample Input Version: ");
+		txtSampleVersion = new Text(grpRangeTest, SWT.BORDER);
+		new Label(grpRangeTest, SWT.NONE).setText("Calculated Version Range: ");
+		lblCalculatedRange = new Label(grpRangeTest, SWT.NONE);
+		
+		new Label(grpRangeTest, SWT.NONE).setText("Test: ");
+		txtMatchVersion = new Text(grpRangeTest, SWT.BORDER);
+		new Label(grpRangeTest, SWT.NONE).setText("Result: ");
+		lblMatchResult = new Label(grpRangeTest, SWT.NONE);
+
+		// Initialize controls
+		loadPreferences(false);
+		updateRadioButtons();
+		
+		txtSampleVersion.setText(sampleVersion.toString());
+		updateCalculatedRange();
+
+		// Add listeners
+		SelectionListener buttonListener = new SelectionListener() {
+			public void widgetSelected(SelectionEvent e) {
+				readRadioButtons();
+				updateCalculatedRange();
+			}
+
+			public void widgetDefaultSelected(SelectionEvent e) {
+				readRadioButtons();
+				updateCalculatedRange();
+			}
+		};
+		btnLowerBoundAny.addSelectionListener(buttonListener);
+		btnLowerBoundMajor.addSelectionListener(buttonListener);
+		btnLowerBoundMinor.addSelectionListener(buttonListener);
+		btnLowerBoundMicro.addSelectionListener(buttonListener);
+		btnLowerBoundExact.addSelectionListener(buttonListener);
+
+		btnUpperBoundAny.addSelectionListener(buttonListener);
+		btnUpperBoundMajor.addSelectionListener(buttonListener);
+		btnUpperBoundMinor.addSelectionListener(buttonListener);
+		btnUpperBoundMicro.addSelectionListener(buttonListener);
+		btnUpperBoundExact.addSelectionListener(buttonListener);
+		
+		txtSampleVersion.addModifyListener(new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				try {
+					sampleVersion = new Version(txtSampleVersion.getText());
+				} catch (IllegalArgumentException x) {
+					sampleVersion = null;
+				}
+				updateCalculatedRange();
+			}
+		});
+		txtMatchVersion.addModifyListener(new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				try {
+					matchVersion = new Version(txtMatchVersion.getText());
+				} catch (IllegalArgumentException x) {
+					matchVersion = null;
+				}
+				updateCalculatedRange();
+			}
+		});
+
+		// Layout
+		GridLayout layout = new GridLayout(1, false);
+		layout.verticalSpacing = 20;
+		composite.setLayout(layout);
+
+		grpLowerBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+		grpLowerBound.setLayout(new GridLayout(1, false));
+
+		grpUpperBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+		grpUpperBound.setLayout(new GridLayout(1, false));
+		
+		grpRangeTest.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
+		grpRangeTest.setLayout(new GridLayout(2, false));
+		
+		txtSampleVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		lblCalculatedRange.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		txtMatchVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		lblMatchResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+		return composite;
+	}
+
+	private void loadPreferences(boolean useDefaults) {
+		IPreferenceStore prefs = getPreferenceStore();
+		String lowerBoundStr;
+		if(useDefaults) {
+			lowerBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+		} else {
+			lowerBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+		}
+		
+		String upperBoundStr;
+		if(useDefaults) {
+			upperBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+		} else {
+			upperBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+		}
+		
+		lowerBoundRule = VersionRangeBoundingRule.valueOf(lowerBoundStr);
+		upperBoundRule = VersionRangeBoundingRule.valueOf(upperBoundStr);
+	}
+
+	private void updateRadioButtons() {
+		switch (lowerBoundRule) {
+		case Exact:
+			btnLowerBoundExact.setSelection(true);
+			btnLowerBoundMicro.setSelection(false);
+			btnLowerBoundMinor.setSelection(false);
+			btnLowerBoundMajor.setSelection(false);
+			btnLowerBoundAny.setSelection(false);
+			break;
+		case Micro:
+			btnLowerBoundExact.setSelection(false);
+			btnLowerBoundMicro.setSelection(true);
+			btnLowerBoundMinor.setSelection(false);
+			btnLowerBoundMajor.setSelection(false);
+			btnLowerBoundAny.setSelection(false);
+			break;
+		case Minor:
+			btnLowerBoundExact.setSelection(false);
+			btnLowerBoundMicro.setSelection(false);
+			btnLowerBoundMinor.setSelection(true);
+			btnLowerBoundMajor.setSelection(false);
+			btnLowerBoundAny.setSelection(false);
+			break;
+		case Major:
+			btnLowerBoundExact.setSelection(false);
+			btnLowerBoundMicro.setSelection(false);
+			btnLowerBoundMinor.setSelection(false);
+			btnLowerBoundMajor.setSelection(true);
+			btnLowerBoundAny.setSelection(false);
+			break;
+		case Any:
+			btnLowerBoundExact.setSelection(false);
+			btnLowerBoundMicro.setSelection(false);
+			btnLowerBoundMinor.setSelection(false);
+			btnLowerBoundMajor.setSelection(false);
+			btnLowerBoundAny.setSelection(true);
+			break;
+		}
+
+		switch (upperBoundRule) {
+		case Exact:
+			btnUpperBoundExact.setSelection(true);
+			btnUpperBoundMicro.setSelection(false);
+			btnUpperBoundMinor.setSelection(false);
+			btnUpperBoundMajor.setSelection(false);
+			btnUpperBoundAny.setSelection(false);
+			break;
+		case Micro:
+			btnUpperBoundExact.setSelection(false);
+			btnUpperBoundMicro.setSelection(true);
+			btnUpperBoundMinor.setSelection(false);
+			btnUpperBoundMajor.setSelection(false);
+			btnUpperBoundAny.setSelection(false);
+			break;
+		case Minor:
+			btnUpperBoundExact.setSelection(false);
+			btnUpperBoundMicro.setSelection(false);
+			btnUpperBoundMinor.setSelection(true);
+			btnUpperBoundMajor.setSelection(false);
+			btnUpperBoundAny.setSelection(false);
+			break;
+		case Major:
+			btnUpperBoundExact.setSelection(false);
+			btnUpperBoundMicro.setSelection(false);
+			btnUpperBoundMinor.setSelection(false);
+			btnUpperBoundMajor.setSelection(true);
+			btnUpperBoundAny.setSelection(false);
+			break;
+		case Any:
+			btnUpperBoundExact.setSelection(false);
+			btnUpperBoundMicro.setSelection(false);
+			btnUpperBoundMinor.setSelection(false);
+			btnUpperBoundMajor.setSelection(false);
+			btnUpperBoundAny.setSelection(true);
+		}
+	}
+
+	private void readRadioButtons() {
+		if (btnLowerBoundExact.getSelection()) {
+			lowerBoundRule = VersionRangeBoundingRule.Exact;
+		} else if (btnLowerBoundMicro.getSelection()) {
+			lowerBoundRule = VersionRangeBoundingRule.Micro;
+		} else if (btnLowerBoundMinor.getSelection()) {
+			lowerBoundRule = VersionRangeBoundingRule.Minor;
+		} else if (btnLowerBoundMajor.getSelection()) {
+			lowerBoundRule = VersionRangeBoundingRule.Major;
+		} else if(btnLowerBoundAny.getSelection()) {
+			lowerBoundRule = VersionRangeBoundingRule.Any;
+		}
+
+		if (btnUpperBoundExact.getSelection()) {
+			upperBoundRule = VersionRangeBoundingRule.Exact;
+		} else if (btnUpperBoundMicro.getSelection()) {
+			upperBoundRule = VersionRangeBoundingRule.Micro;
+		} else if (btnUpperBoundMinor.getSelection()) {
+			upperBoundRule = VersionRangeBoundingRule.Minor;
+		} else if (btnUpperBoundMajor.getSelection()) {
+			upperBoundRule = VersionRangeBoundingRule.Major;
+		} else if(btnUpperBoundAny.getSelection()) {
+			upperBoundRule = VersionRangeBoundingRule.Any;
+		}
+	}
+
+	private void updateCalculatedRange() {
+		VersionRange range;
+		String rangeStr;
+		String matchResult;
+		
+		if(sampleVersion == null) {
+			range = null;
+			rangeStr = "";
+		} else {
+			range = VersionRange.newInstance(sampleVersion, lowerBoundRule, upperBoundRule);
+			rangeStr = range.toString();
+		}
+		lblCalculatedRange.setText(rangeStr);
+		
+		if(matchVersion == null || range == null) {
+			matchResult = "";
+		} else {
+			matchResult = range.contains(matchVersion) ? "MATCH!" : "No Match";
+		}
+		lblMatchResult.setText(matchResult);
+	}
+
+	public void init(IWorkbench workbench) {
+		this.workbench = workbench;
+	}
+
+	@Override
+	protected IPreferenceStore doGetPreferenceStore() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+
+	@Override
+	public boolean performOk() {
+		getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_LOWER_BOUND, lowerBoundRule.name());
+		getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_UPPER_BOUND, upperBoundRule.name());
+
+		return true;
+	}
+	
+	@Override
+	protected void performDefaults() {
+		super.performDefaults();
+		loadPreferences(true);
+		updateRadioButtons();
+		updateCalculatedRange();
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
new file mode 100644
index 0000000..9e205d3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.installs;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstallType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class OSGiInstallsPreferencePage extends ProjectDependentPreferencesPage implements
+		IWorkbenchPreferencePage {
+
+	private class Install {
+		private String id;
+		private String location;
+		private IOSGiInstallType type;
+		
+		private Install(String id, String location) {
+			this.id = id;
+			this.location = location;
+		}
+
+		private IOSGiInstallType getType() {
+			if ( type == null ) {
+				type = SigilCore.getInstallManager().findInstallType(location);
+			}
+			return type;
+		}
+	}
+
+	private HashMap<String, Install> installs = new HashMap<String, Install>();
+	private CheckboxTableViewer viewer;
+	private boolean changed;
+	
+	public OSGiInstallsPreferencePage() {
+		super("OSGi Installs");
+	}
+	
+	public void init(IWorkbench workbench) {
+	}
+
+
+	@Override
+	protected Control createContents(Composite parent) {
+		Composite control = new Composite(parent, SWT.NONE);
+		
+		buildComponents(control);
+		
+		load();
+		
+		checkValid();
+		
+		return control;
+	}
+
+	@Override
+	protected boolean isDirty() {
+		return changed;
+	}
+
+	
+	private void buildComponents(Composite control) {
+		new Label(control, SWT.NONE).setText("Installs:");
+		new Label(control, SWT.NONE); // padding
+		
+		Table table = new Table(control, SWT.CHECK | SWT.SINGLE | SWT.BORDER);
+		
+		Button add = new Button(control, SWT.PUSH);
+		add.setText("Add");
+		add.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				add();
+			}			
+		});
+		
+		final Button remove = new Button(control, SWT.PUSH);
+		remove.setEnabled(false);
+		remove.setText("Remove");
+		remove.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				remove();
+			}			
+		});
+		
+		// viewers
+		viewer = new CheckboxTableViewer(table);
+		viewer.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}			
+		});
+				
+		viewer.setLabelProvider( new LabelProvider() {
+			@Override
+			public String getText(Object element) {
+				Install i = (Install) element;
+				IOSGiInstallType type = i.getType();
+				if ( type == null ) {
+					return "<invalid> [" + i.location + "]";
+				}
+				else {
+					return type.getName() + " " + type.getVersion() + " [" + i.location + "]";
+				}
+			}
+
+			@Override
+			public Image getImage(Object element) {
+				Install i = (Install) element;
+				IOSGiInstallType type = i.getType();
+				
+				if (type == null) {
+					return null;
+				} else {
+				return type.getIcon();
+				}
+			}			
+		});
+		
+		viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				boolean enabled = !event.getSelection().isEmpty();
+				remove.setEnabled(enabled );
+			}
+		});
+		
+		viewer.addCheckStateListener( new ICheckStateListener () {
+			public void checkStateChanged(CheckStateChangedEvent event) {
+				if ( event.getChecked() ) {
+					changed = true;
+				}
+				viewer.setCheckedElements( new Object[] { event.getElement() } );
+			}
+		});
+		
+		viewer.setInput(installs.values());
+		
+		// layout
+		control.setLayout( new GridLayout(2, false) );
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+		add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));		
+	}
+
+	private void load() {
+		String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+		if ( pref != null && pref.length() > 0 ) {
+			for ( String id : pref.split(",") ) {
+				String loc = getPreferenceStore().getString( SigilCore.OSGI_INSTALL_PREFIX + id );
+				installs.put( id, new Install( id, loc ) );
+			}
+		}
+		
+		viewer.refresh();
+		
+		if ( !installs.isEmpty() ) {
+			String defId = getPreferenceStore().getString( SigilCore.OSGI_DEFAULT_INSTALL_ID );
+			if ( defId == null || defId.trim().length() == 0 ) {
+				viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+			}
+			else {
+				viewer.setCheckedElements( new Object[] { installs.get( defId ) } );
+			}
+		}
+	}
+	
+	protected void doSave() {
+		// zero out old configs
+		String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+		if ( pref != null && pref.length() > 0 ) {
+			for ( String id : pref.split(",") ) {
+				getPreferenceStore().setToDefault( SigilCore.OSGI_INSTALL_PREFIX + id );
+			}
+		}
+		
+		// store new configs
+		if ( installs.isEmpty() ) {
+			getPreferenceStore().setToDefault(SigilCore.OSGI_INSTALLS);
+			getPreferenceStore().setToDefault(SigilCore.OSGI_DEFAULT_INSTALL_ID);
+		}
+		else {
+			StringBuffer buf = new StringBuffer();
+			for (Install i : installs.values() ) {
+				if ( buf.length() > 0 ) {
+					buf.append(",");
+				}
+				buf.append( i.id );
+				getPreferenceStore().setValue( SigilCore.OSGI_INSTALL_PREFIX + i.id, i.location );
+			}
+			
+			getPreferenceStore().setValue( SigilCore.OSGI_INSTALLS, buf.toString() );
+			Install def = (Install) viewer.getCheckedElements()[0];
+			getPreferenceStore().setValue(SigilCore.OSGI_DEFAULT_INSTALL_ID, def.id);
+		}
+		changed = false;
+	}
+
+	private boolean isOK() {
+		return installs.isEmpty() || viewer.getCheckedElements().length > 0;
+	}
+	
+	private void add() {
+		Shell shell = SigilUI.getActiveWorkbenchShell();
+		DirectoryDialog dialog = new DirectoryDialog(shell);
+		String dir = dialog.open();
+		if ( dir != null ) {
+			Install install = new Install( UUID.randomUUID().toString(), dir );
+			if ( install.getType() == null ) {
+				MessageDialog.openError(shell, "Error", "Invalid OSGi install directory" );
+			}
+			else {
+				boolean empty = installs.isEmpty();
+				
+				installs.put( install.id, install );
+				viewer.refresh();
+				
+				if ( empty ) {
+					viewer.setCheckedElements( new Object[] { install } );
+				}
+				
+				checkValid();
+				changed = true;
+			}
+		}
+	}
+
+	private void checkValid() {
+		if ( isOK() ) {
+			setErrorMessage(null);
+			setValid(true);
+		}
+		else {
+			setErrorMessage("Missing default OSGi install");
+			setValid(false);
+		}
+	}
+
+	private void remove() {
+		IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
+		Install i = (Install) sel.getFirstElement();
+		boolean def = viewer.getChecked(i);
+		installs.remove(i.id);
+		viewer.refresh();
+		if ( def && installs.size() > 0 ) {
+			viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+		}
+		checkValid();
+		changed = true;
+	}
+
+	@Override
+	protected IPreferenceStore doGetPreferenceStore() {
+		return SigilCore.getDefault().getPreferenceStore();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
new file mode 100644
index 0000000..cbfa165
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.project;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.osgi.service.prefs.BackingStoreException;
+
+public class ProjectPropertyPage extends PropertyPage implements
+		IWorkbenchPropertyPage {
+
+	private boolean projectSpecific;
+	private ComboViewer setView;
+	private Composite settings;
+	private Button projectSpecificBtn;
+
+	@Override
+	protected Control createContents(Composite parent) {
+		final Composite control = new Composite(parent, SWT.NONE);
+		
+		projectSpecificBtn = new Button(control, SWT.CHECK);
+		projectSpecificBtn.setText( "Enable project specific settings");
+		projectSpecificBtn.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				setProjectSpecific(!projectSpecific);
+			}
+		});
+		
+		Label link = new Label( control, SWT.UNDERLINE_SINGLE );
+		link.addMouseListener( new MouseAdapter() {
+			@Override
+			public void mouseDown(MouseEvent e) {
+				PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, SigilCore.REPOSITORIES_PREFERENCES_ID, null, null);
+				dialog.open();
+			}			
+		});
+		
+		link.setText("Configure workspace settings" );
+		
+		settings = new Composite( control, SWT.BORDER );
+		settings.setLayout( new GridLayout(1,false));
+		createSettings( settings );
+		
+		setFonts(control);
+		
+		// layout
+		control.setLayout( new GridLayout(2, false));
+		projectSpecificBtn.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false ) );
+		settings.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1 ) );
+
+		// load settings
+		String currentSet = getCurrentSet();
+		
+		if ( currentSet == null ) {
+			setProjectSpecific(false);
+		}
+		else {
+			setView.setSelection( new StructuredSelection(currentSet) );
+			setProjectSpecific(true);
+		}
+		
+		return control;
+	}
+
+	private void setFonts(Composite control) {
+		Composite p = control.getParent();
+		for ( Control c : control.getChildren() ) {
+			c.setFont( p.getFont() );
+			if ( c instanceof Composite ) {
+				setFonts( (Composite) c );
+			}
+		}
+	}
+
+	private void setProjectSpecific(boolean projectSpecific) {
+		if ( this.projectSpecific != projectSpecific ) {
+			this.projectSpecific = projectSpecific;
+			settings.setEnabled(projectSpecific);
+			for ( Control c : settings.getChildren() ) {
+				c.setEnabled( projectSpecific );
+			}
+			projectSpecificBtn.setSelection(projectSpecific);
+		}
+	}
+
+	private void createSettings(Composite parent) {
+		Composite control = new Composite(parent, SWT.NONE);
+		
+		new Label( control, SWT.NONE).setText( "Repository Set:" );
+		Combo combo = new Combo(control, SWT.SINGLE);
+		
+		setView = new ComboViewer(combo);
+		setView.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		});
+		
+		setView.setInput( SigilCore.getRepositoryConfiguration().loadRepositorySets().keySet() );
+		
+		// layout
+		control.setLayout( new GridLayout(2, false ) );
+	}
+
+	private String getCurrentSet() {
+		try {
+			IProject p = (IProject) getElement().getAdapter(IProject.class);
+			ISigilProjectModel model = SigilCore.create(p);
+			return model.getPreferences().get( SigilCore.REPOSITORY_SET, null );
+		} catch (CoreException e) {
+			SigilCore.error("Failed to read repository set", e );
+			return null;
+		}
+	}
+
+	@Override
+	public boolean okToLeave() {
+		if ( projectSpecific ) {
+			if ( setView.getSelection().isEmpty() ) {
+				setErrorMessage("Must select a repository set");
+				return false;
+			}
+		}
+		setErrorMessage(null);
+		return true;
+	}
+
+	@Override
+	public boolean performOk() {
+		return ProjectUtils.runTaskWithRebuildCheck( new Callable<Boolean>() {
+			public Boolean call() throws CoreException, BackingStoreException {
+				String set = null;
+				if ( projectSpecific ) {
+					set = (String) ((IStructuredSelection) setView.getSelection()).getFirstElement();
+				}
+
+				IProject p = (IProject) getElement().getAdapter(IProject.class);
+				ISigilProjectModel model = SigilCore.create(p);
+				model.getPreferences().put( SigilCore.REPOSITORY_SET, set );
+				model.getPreferences().flush();
+				return true;
+			}
+
+		}, getShell());
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
new file mode 100644
index 0000000..bf410ae
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.Wizard;
+
+public class NewRepositoryWizard extends Wizard {
+
+	private IRepositoryModel repository;
+	
+	private RepositoryTypeSelectionPage page = new RepositoryTypeSelectionPage();
+	
+	public void addPages() {
+		addPage( page );
+	}
+	
+	@Override
+	public boolean performFinish() {
+		repository = page.getRepository();
+		return true;
+	}
+	@Override
+	public boolean needsPreviousAndNextButtons() {
+		return true;
+	}
+
+	public IRepositoryModel getRepository() {
+		return repository;
+	}
+
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
new file mode 100644
index 0000000..9503a98
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;

+

+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+

+public class RepositoriesPreferencePage extends ProjectDependentPreferencesPage implements

+		IWorkbenchPreferencePage {

+
+	private boolean changed;
+	private RepositoriesView viewPage;
+	private RepositorySetsView setPage;
+
+	public RepositoriesPreferencePage() {
+		super( "Repository Preferences" );
+	}
+	@Override

+	protected Control createContents(Composite parent) {

+		Control control = initContents( parent );
+		return control;

+	}

+

+	@Override

+	protected IPreferenceStore doGetPreferenceStore() {

+		return SigilCore.getDefault().getPreferenceStore();

+	}

+
+	protected void changed() {
+		changed = true;
+		updateApplyButton();
+	}

+	
+	private Control initContents(Composite parent) {
+		viewPage = new RepositoriesView(this);
+		setPage = new RepositorySetsView(this);
+		
+		Composite control = new Composite(parent, SWT.NONE);
+		
+		TabFolder folder = new TabFolder(control, SWT.TOP);
+		
+		TabItem view = new TabItem(folder, SWT.NONE);
+		view.setText("Repositories");
+		view.setControl(viewPage.createContents(folder) );
+		
+		TabItem sets = new TabItem(folder, SWT.NONE);
+		sets.setText("Sets");
+		sets.setControl(setPage.createContents(folder) );
+
+		control.setLayout(new GridLayout(1, true));
+		folder.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true));
+		
+		return control;
+	}
+
+	public void init(IWorkbench workbench) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	@Override
+	protected void doSave() {
+		try {
+			IRepositoryConfiguration config = SigilCore.getRepositoryConfiguration();
+			config.saveRepositories(viewPage.getRepositories());
+			config.saveRepositorySets(setPage.getSets());
+			IRepositorySet defaultSet = new RepositorySet(setPage.getDefaultRepositories());
+			config.setDefaultRepositorySet(defaultSet);
+			
+			setErrorMessage(null);
+			getApplyButton().setEnabled(false);
+			changed = false;
+		} catch (CoreException e) {
+			setErrorMessage("Failed to save repositories:" + e.getStatus().getMessage());
+			SigilCore.error("Failed to save repositories", e);
+		}
+	}
+
+	@Override
+	protected boolean isDirty() {
+		return changed;
+	}	

+}

diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
new file mode 100644
index 0000000..f9394e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.actions.RefreshRepositoryAction;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoriesView {
+	private final RepositoriesPreferencePage page;
+
+	private List<IRepositoryModel> repositories;
+	
+	private TableViewer repositoryView;
+
+	public RepositoriesView(RepositoriesPreferencePage page) {
+		this.page = page;
+	}
+
+	public Control createContents(Composite parent) {
+		// Create Controls
+		Composite composite = new Composite(parent, SWT.NONE);
+		
+		Table table = new Table(composite, SWT.MULTI | SWT.BORDER);
+
+		// Table Viewer Setup
+		repositoryView = new TableViewer(table);
+		repositoryView.setLabelProvider(new LabelProvider() {
+			@Override
+			public String getText(Object element) {
+				IRepositoryModel rep = (IRepositoryModel) element;
+				return rep.getName();
+			}
+
+			@Override
+			public Image getImage(Object element) {
+				IRepositoryModel rep = (IRepositoryModel) element;				
+				return rep.getType().getIcon();
+			}
+		});
+
+		repositoryView.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		} );
+		
+		// Layout
+		composite.setLayout(new GridLayout(2, false));
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+		
+		createButtons(composite, repositoryView);	
+		
+		repositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+		repositoryView.setInput(repositories);		
+		
+		return composite;
+	}
+	
+	private void createButtons(final Composite composite, final TableViewer repositoryView) {
+		final Button add = new Button(composite, SWT.PUSH);
+		add.setText("Add...");
+		add.setEnabled(true);
+		
+		final Button edit = new Button(composite, SWT.PUSH);
+		edit.setText("Edit...");
+		edit.setEnabled(false);
+				
+		final Button remove = new Button(composite, SWT.PUSH);
+		remove.setText("Remove");
+		remove.setEnabled(false);
+		
+		final Button refresh = new Button(composite, SWT.PUSH);
+		refresh.setText("Refresh");
+		refresh.setEnabled( false );
+		
+		// Listeners
+		add.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				add(composite);
+			}			
+		});
+		
+		edit.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+				edit(composite, sel);
+			}			
+		});
+		
+		remove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+				remove(sel);
+			}			
+		});
+		
+		refresh.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+				refresh(composite, sel);
+			}			
+		});
+		
+		repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				boolean selected = !event.getSelection().isEmpty();
+				if ( selected ) {
+					refresh.setEnabled(true);
+					
+					IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+					
+					checkEditEnabled(edit, sel);
+					checkRemoveEnabled(remove, sel);
+				}
+				else {
+					refresh.setEnabled(false);
+					edit.setEnabled(false);
+					remove.setEnabled(false);
+				}
+			}
+		});
+		
+		add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+	}
+	
+	@SuppressWarnings("unchecked")
+	private void checkRemoveEnabled(Button button, IStructuredSelection sel) {
+		boolean alldynamic = true;
+		for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+			IRepositoryModel model = (IRepositoryModel) i.next();
+			if ( !model.getType().isDynamic() ) {
+				alldynamic = false;
+				break;
+			}
+		}
+		button.setEnabled(alldynamic);
+	}
+
+	private void checkEditEnabled(Button edit, IStructuredSelection sel) {
+		if ( sel.size() == 1 ) {
+			IRepositoryModel element = (IRepositoryModel) sel.getFirstElement();
+			if ( WizardHelper.hasWizard( element.getType()) ) {
+				edit.setEnabled(true);						
+			}
+			else {
+				edit.setEnabled(false);
+			}
+		}
+		else {
+			edit.setEnabled(false);
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	protected void refresh(Control parent, IStructuredSelection sel) {
+		ArrayList<IRepositoryModel> models = new ArrayList<IRepositoryModel>(sel.size());
+		
+		for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+			IRepositoryModel model = (IRepositoryModel) i.next();
+			models.add( model );
+		}
+		
+		new RefreshRepositoryAction(models.toArray(new IRepositoryModel[models.size()])).run();
+	}
+
+	private void add(Control parent) {
+		NewRepositoryWizard wizard = new NewRepositoryWizard();
+		WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+		if ( dialog.open() == Window.OK ) {
+			repositories.add(wizard.getRepository());
+			updated();
+		}
+	}
+
+	private void edit(Control parent, IStructuredSelection sel) {
+		IRepositoryModel model = (IRepositoryModel) sel.getFirstElement();
+		try {
+			RepositoryWizard wizard = WizardHelper.loadWizard(model.getType());
+			wizard.init(model);
+			WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+			if ( dialog.open() == Window.OK ) {
+				updated();
+			}
+		}
+		catch (CoreException e) {
+			SigilCore.error( "Failed to load wizard", e);
+			MessageDialog.openError(getShell(parent), "Error", "Failed to load wizard:" + e.getStatus().getMessage() );
+		}
+	}
+
+	private Shell getShell(Control parent) {
+		return parent.getShell();
+	}
+
+	@SuppressWarnings("unchecked")
+	private void remove(IStructuredSelection sel) {
+		boolean change = false;
+		for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+			change = repositories.remove(i.next());
+		}
+		
+		if ( change ) {
+			updated();
+		}
+	}
+
+	private void updated() {
+		repositoryView.refresh();
+		page.changed();
+	}
+
+	public List<IRepositoryModel> getRepositories() {
+		return repositories;
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
new file mode 100644
index 0000000..8136f55
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class RepositorySetDialog extends TitleAreaDialog {
+	
+	private CheckboxTableViewer viewer;
+	private Text nameTxt;
+	private Button upBtn;
+	private Button downBtn;
+	private final String setName;
+	private List<IRepositoryModel> repositories;
+	private final boolean nameEditable;
+	private final Set<String> set;
+	
+	private String newName;
+	
+	public RepositorySetDialog(Shell shell, Set<String> set) {
+		this(shell, null, true, set);
+	}
+	
+	public RepositorySetDialog(Shell parent, RepositoryViewData data, boolean nameEditable, Set<String> set) {
+		super(parent);
+		this.set = set;
+		this.setName = data == null ? "" : data.getName();
+		this.repositories = data == null ? new ArrayList<IRepositoryModel>() : new ArrayList<IRepositoryModel>(Arrays.asList(data.getRepositories()));
+		this.nameEditable = nameEditable;
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		Composite area = (Composite) super.createDialogArea(parent);
+		createControl(area);
+		return area;
+	}
+
+	public void createControl(Composite parent) {
+		// controls
+		Composite body = new Composite(parent, SWT.NONE);
+		body.setLayoutData( new GridData(GridData.FILL_BOTH) );
+		
+		if ( nameEditable ) {
+			new Label( body, SWT.NONE ).setText( "Name" );
+			
+			nameTxt = new Text( body, SWT.BORDER );
+			
+			nameTxt.setText(setName);
+			
+			nameTxt.addKeyListener( new KeyAdapter() {
+				@Override
+				public void keyReleased(KeyEvent e) {
+					checkComplete();
+				}
+			});
+		}
+		
+		Composite table = new Composite(body, SWT.NONE);
+		table.setLayout( new GridLayout(2, false ) );
+		createTable( table );
+		
+		// layout
+		body.setLayout( new GridLayout( 2, false ) );
+		if ( nameEditable ) {
+			nameTxt.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false) );
+		}
+		table.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1) );
+	}
+	
+	public RepositoryViewData getData() {
+		String name = nameEditable ? newName : setName;
+		IRepositoryModel[] reps = repositories.toArray( new IRepositoryModel[repositories.size()]);
+		return new RepositoryViewData( name, reps );
+	}
+
+	private void checkComplete() {
+		if ( nameEditable ) {
+			String name = nameTxt.getText();
+			if ( !name.equals( setName ) && set.contains( name ) ) {
+				setErrorMessage("Set " + name + " already exists" );
+				Button b = getButton(IDialogConstants.OK_ID);
+				b.setEnabled(false);
+			}
+		}		
+		setErrorMessage(null);
+		Button b = getButton(IDialogConstants.OK_ID);
+		b.setEnabled(true);
+	}
+
+
+	@Override
+	protected void okPressed() {
+		if ( nameEditable ) {
+			newName = nameTxt.getText();
+		}
+		repositories = getRepositories();
+		super.okPressed();
+	}
+
+	private void createTable(Composite body) {
+		createViewer(body);
+		
+		Composite btns = new Composite(body, SWT.NONE);
+		btns.setLayout( new GridLayout( 1, true ) );
+		
+		createButtons(btns);
+		
+		// layout
+		viewer.getTable().setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) );
+		btns.setLayoutData( new GridData( SWT.RIGHT, SWT.TOP, false, false ) );
+	}
+
+	private void createButtons(Composite parent) {
+		upBtn = new Button(parent, SWT.PUSH);
+		upBtn.setText( "Up" );
+		upBtn.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				up();
+			}
+		});
+		
+		downBtn = new Button(parent, SWT.PUSH);
+		downBtn.setText( "Down" );
+		downBtn.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				down();
+			}
+		});
+		
+		setUpDownEnabled(false);
+	}
+
+	private void up() {
+		IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+		int i = repositories.indexOf(model);
+		if ( i > 0 ) {
+			repositories.remove( i );
+			repositories.add( i - 1, model );
+			viewer.refresh();
+		}
+	}
+
+	private void down() {
+		IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+		int i = repositories.indexOf(model);
+		if ( i < repositories.size() - 1 ) {
+			repositories.remove( i );
+			repositories.add( i + 1, model );
+			viewer.refresh();
+		}
+	}
+
+	private void createViewer(Composite parent) {
+		viewer = CheckboxTableViewer.newCheckList(parent, SWT.BORDER);
+		
+		viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				setUpDownEnabled( !viewer.getSelection().isEmpty() ); 
+			}
+		});
+		
+		viewer.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		});
+		
+		viewer.setLabelProvider( new DefaultLabelProvider() {
+			public Image getImage(Object element) {
+				return null;
+			}
+
+			public String getText(Object element) {
+				IRepositoryModel m = (IRepositoryModel) element;
+				return m.getName();
+			}			
+		});
+		
+		viewer.setInput( repositories );
+		
+		for ( IRepositoryModel m : repositories ) {
+			viewer.setChecked(m, true);
+		}
+		
+		List<IRepositoryModel> allRepositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+		
+		for ( IRepositoryModel m : allRepositories ) {
+			if ( !repositories.contains(m) ) {
+				repositories.add(m);
+			}
+		}
+		
+		viewer.refresh();		
+	}
+
+	private void setUpDownEnabled(boolean enabled) {
+		upBtn.setEnabled(enabled);
+		downBtn.setEnabled(enabled);
+	}
+
+	private List<IRepositoryModel> getRepositories() {
+		ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+		
+		for ( IRepositoryModel m : repositories ) {
+			if ( viewer.getChecked(m) ) {
+				reps.add( m );
+			}
+		}
+		
+		return reps;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
new file mode 100644
index 0000000..0470675
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositorySetsView {
+	private static final String DEFAULT = "default";
+
+	private final RepositoriesPreferencePage page;
+
+	private ArrayList<RepositoryViewData> sets = new ArrayList<RepositoryViewData>();
+	
+	private TableViewer setView;
+
+	private RepositoryViewData defaultSet;
+	
+	public RepositorySetsView(RepositoriesPreferencePage page) {
+		this.page = page;
+	}
+
+	public Control createContents(Composite parent) {
+		// Create Controls
+		Composite composite = new Composite(parent, SWT.NONE);
+		
+		Table table = new Table(composite, SWT.SINGLE | SWT.BORDER);
+
+		// Table Viewer Setup
+		setView = new TableViewer(table);
+
+		setView.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		} );
+		
+		defaultSet = new RepositoryViewData(DEFAULT, SigilCore.getRepositoryConfiguration().getDefaultRepositorySet().getRepositories());
+		
+		sets.add( defaultSet );
+		
+		for( Map.Entry<String, IRepositorySet> e : SigilCore.getRepositoryConfiguration().loadRepositorySets().entrySet() ) {
+			IRepositorySet s = e.getValue();
+			sets.add( new RepositoryViewData( e.getKey(), s.getRepositories() ) );
+		}
+		
+		setView.setLabelProvider( new DefaultLabelProvider() {
+			public Image getImage(Object element) {
+				return null;
+			}
+
+			public String getText(Object element) {
+				RepositoryViewData data = (RepositoryViewData) element;
+				return data.getName();
+			}
+		});
+		
+		setView.setInput(sets);
+		
+		// Layout
+		composite.setLayout(new GridLayout(2, false));
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+		
+		createButtons(composite);	
+				
+		return composite;
+	}
+	
+	private void createButtons(final Composite composite) {
+		final Button add = new Button(composite, SWT.PUSH);
+		add.setText("Add...");
+		add.setEnabled(true);
+		
+		final Button edit = new Button(composite, SWT.PUSH);
+		edit.setText("Edit...");
+		edit.setEnabled(false);
+				
+		final Button remove = new Button(composite, SWT.PUSH);
+		remove.setText("Remove");
+		remove.setEnabled(false);
+		// Listeners
+		add.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				add(composite);
+			}			
+		});
+		
+		edit.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+				edit(composite, sel);
+			}			
+		});
+		
+		remove.addSelectionListener( new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+				remove(sel);
+			}			
+		});
+
+		setView.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				boolean enabled = !event.getSelection().isEmpty();
+				if ( enabled ) {
+					RepositoryViewData element = (RepositoryViewData) ((IStructuredSelection) event.getSelection()).getFirstElement();
+					edit.setEnabled(true);
+					remove.setEnabled( element != defaultSet );
+				}
+				else {
+					edit.setEnabled(false);
+					remove.setEnabled(false);
+				}
+			}
+		});
+		
+		add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+		remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));		
+	}
+	
+	private void add(Control parent) {
+		RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), getNames());
+		if ( wizard.open() == Window.OK ) {
+			sets.add( wizard.getData() );
+			updated();
+		}
+	}
+
+	private void edit(Control parent, IStructuredSelection sel) {
+		RepositoryViewData data = (RepositoryViewData) sel.getFirstElement();
+		RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), data, data != defaultSet, getNames());
+		if ( wizard.open() == Window.OK ) {
+			if ( data != defaultSet ) {
+				data.setName( wizard.getData().getName() );
+			}
+			data.setRepositories( wizard.getData().getRepositories() );
+			updated();
+		}
+	}
+
+	private Set<String> getNames() {
+		HashSet<String> names = new HashSet<String>();
+		
+		for ( RepositoryViewData view : sets ) {
+			if ( view != defaultSet ) {
+				names.add( view.getName() );
+			}
+		}
+		
+		return names;
+	}
+
+	private Shell getShell(Control parent) {
+		return parent.getShell();
+	}
+
+	private void remove(IStructuredSelection sel) {
+		if ( sets.remove(sel.getFirstElement())) {
+			updated();
+		}
+	}
+
+	private void updated() {
+		setView.refresh();
+		page.changed();
+	}
+
+	public Map<String, IRepositorySet> getSets() {
+		HashMap<String, IRepositorySet> ret = new HashMap<String, IRepositorySet>();
+		
+		for ( RepositoryViewData data : sets ) {
+			if ( data != defaultSet ) {
+				IRepositorySet set = new RepositorySet(data.getRepositories());
+				ret.put( data.getName(), set );
+			}
+		}
+		
+		return ret;
+	}
+
+	public IRepositoryModel[] getDefaultRepositories() {
+		return defaultSet.getRepositories();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
new file mode 100644
index 0000000..3dfe8b8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardSelectionPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoryTypeSelectionPage extends WizardSelectionPage implements IWizardPage {
+
+	private static final String TITLE = "Select Repository";
+	
+	private TableViewer repositoryView;
+	private IRepositoryModel repositoryElement;
+	
+	public RepositoryTypeSelectionPage() {
+		super(TITLE);
+		setTitle(TITLE);
+	}
+
+	public void createControl(Composite parent) {
+		Composite control = new Composite(parent, SWT.NONE);
+		
+		// components
+		new Label(control, SWT.NONE).setText("Repositories" );
+		Table table = new Table(control, SWT.SINGLE | SWT.BORDER);
+		
+		// layout
+		control.setLayout( new GridLayout(1, true) );
+		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		
+		// view
+		repositoryView = new TableViewer(table);
+		repositoryView.setLabelProvider( new LabelProvider() {
+			@Override
+			public String getText(Object element) {
+				IRepositoryType rep = (IRepositoryType) element;
+				return rep.getType();
+			}
+
+			@Override
+			public Image getImage(Object element) {
+				IRepositoryType rep = (IRepositoryType) element;
+				return rep.getIcon();
+			}			
+		});
+		
+		repositoryView.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		} );
+		
+		repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				if ( !event.getSelection().isEmpty() ) {
+					IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+					IRepositoryType type = (IRepositoryType) sel.getFirstElement();
+					repositoryElement = SigilCore.getRepositoryConfiguration().newRepositoryElement(type);
+					selectWizardNode(new RepositoryWizardNode(repositoryElement));
+				}
+			}
+		});
+		
+		ArrayList<IRepositoryType> descriptors = new ArrayList<IRepositoryType>(SigilCore.getRepositoryConfiguration().loadRepositoryTypes());
+		
+		for ( Iterator<IRepositoryType> i = descriptors.iterator(); i.hasNext(); ) {
+			if ( !i.next().isDynamic() ) {
+				i.remove();
+			}
+		}
+		
+		repositoryView.setInput( descriptors );
+		
+		setControl(control);
+	}
+	
+	public void selectWizardNode(RepositoryWizardNode node) {
+		setSelectedNode(node);
+	}
+	
+	public RepositoryWizardNode getSelectedWizardNode() {
+		return (RepositoryWizardNode) getSelectedNode();
+	}
+
+	public IRepositoryModel getRepository() {		
+		return repositoryElement;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
new file mode 100644
index 0000000..85124c7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+
+class RepositoryViewData {
+	private String name;
+	private IRepositoryModel[] reps;
+
+	RepositoryViewData(String name, IRepositoryModel[] reps) {
+		this.name = name;
+		this.reps = reps;
+	}
+
+	String getName() {
+		return name;
+	}
+
+	IRepositoryModel[] getRepositories() {
+		return reps;
+	}
+	
+	void setName(String name) {
+		this.name = name;
+	}
+
+	void setRepositories(IRepositoryModel[] reps) {
+		this.reps = reps;
+	}
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
new file mode 100644
index 0000000..5ea458a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardNode;
+import org.eclipse.swt.graphics.Point;
+
+public class RepositoryWizardNode implements IWizardNode {
+
+	private IRepositoryModel repository;
+	
+	private RepositoryWizard wizard;
+	
+	public RepositoryWizardNode(IRepositoryModel repository) {
+		this.repository = repository;
+	}
+
+	public void dispose() {
+		if ( wizard != null ) {
+			wizard.dispose();
+			wizard = null;
+		}
+	}
+
+	public Point getExtent() {
+		return new Point(-1, -1);
+	}
+
+	public IWizard getWizard() {
+		if ( wizard == null ) {
+			try {
+				wizard = WizardHelper.loadWizard(repository.getType());
+				wizard.init( repository );
+			} catch (CoreException e) {
+				SigilCore.error( "Failed to create wizard for " + repository.getType(), e);
+			}
+		}
+		return wizard;
+	}
+	
+	public IRepositoryModel getRepository() {
+		return repository;
+	}
+
+	public boolean isContentCreated() {
+		return wizard != null;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
new file mode 100644
index 0000000..101b57e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+public class WizardHelper {
+	public static RepositoryWizard loadWizard(IRepositoryType type) throws CoreException {
+		IConfigurationElement e = findWizardConfig(type.getId());
+		
+		if ( e == null ) {
+			throw SigilCore.newCoreException("No wizard registered for repository " + type, null);
+		}
+		
+		return (RepositoryWizard) e.createExecutableExtension("class");
+	}
+	
+	public static boolean hasWizard(IRepositoryType type) {
+		return findWizardConfig(type.getId()) != null;
+	}	
+		
+	private static IConfigurationElement findWizardConfig(String id) {
+		IExtensionRegistry registry = Platform.getExtensionRegistry();		
+		IExtensionPoint p = registry.getExtensionPoint(SigilUI.REPOSITORY_WIZARD_EXTENSION_POINT_ID);
+		
+		for ( IExtension e : p.getExtensions() ) {
+			for ( IConfigurationElement c : e.getConfigurationElements() ) {
+				if ( id.equals( c.getAttribute("repository") ) ) {
+					return c;
+				}
+			}
+		}
+		
+		return null;
+	}
+	
+	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
new file mode 100644
index 0000000..163154d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportPackageProposal implements IJavaCompletionProposal {
+
+	private IPackageExport e;
+	private ISigilProjectModel n;
+	
+	public ImportPackageProposal(IPackageExport e, ISigilProjectModel n) {
+		this.e = e;
+		this.n = n;
+	}
+
+	public int getRelevance() {
+		return 100;
+	}
+
+	public void apply(IDocument document) {
+		try {
+			
+			final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+			i.setPackageName(e.getPackageName());
+			IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+			VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+			VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+			
+			Version version = e.getVersion();
+			VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+			i.setVersions( selectedVersions );
+			
+			WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+				@Override
+				protected void execute(IProgressMonitor monitor)
+						throws CoreException {
+					n.getBundle().getBundleInfo().addImport( i );
+					n.save(null);
+				}
+			};
+			
+			SigilUI.runWorkspaceOperation(op, null);
+		} catch (ModelElementFactoryException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public String getAdditionalProposalInfo() {
+		return null;
+	}
+
+	public IContextInformation getContextInformation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public String getDisplayString() {
+		return "Import package " + e.getPackageName() + " version " + e.getVersion() + " to bundle";
+	}
+
+	public Image getImage() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public Point getSelection(IDocument document) {
+		return null;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
new file mode 100644
index 0000000..ac3f9ba
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.search.SigilSearch;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.ui.text.java.IInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;
+
+@SuppressWarnings("restriction")
+public class ImportQuickFixProcessor implements IQuickFixProcessor {
+
+	private static final Object SYNC_FLAG = new Object();
+	
+	public boolean hasCorrections(ICompilationUnit unit, int problemId) {
+		switch ( problemId ) {
+		case IProblem.ImportNotFound:
+		case IProblem.ForbiddenReference:
+		case IProblem.NotVisibleType:
+		case IProblem.UndefinedType:
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
+			IProblemLocation[] locations) throws CoreException {
+		try {
+			HashMap<Object, IJavaCompletionProposal> results = new HashMap<Object, IJavaCompletionProposal>();
+			
+			ISigilProjectModel project = findProject(context);
+			
+			if ( project != null ) {
+				for ( int i = 0; i < locations.length; i++ ) {
+					switch ( locations[i].getProblemId() ) {
+					case IProblem.ForbiddenReference:
+						handleImportNotFound(project, context, locations[i], results);
+						break;
+					case IProblem.ImportNotFound:
+						handleImportNotFound(project, context, locations[i], results);
+						break;
+					case IProblem.IsClassPathCorrect:
+						handleIsClassPathCorrect(project, context, locations[i], results);
+						break;
+					case IProblem.UndefinedType:
+						handleUndefinedType(project, context, locations[i], results);
+						break;
+					case IProblem.UndefinedName:
+						handleUndefinedName(project, context, locations[i], results);
+						break;
+					}
+				}
+			}		
+			
+			return (IJavaCompletionProposal[])results.values().toArray(new IJavaCompletionProposal[results.size()]);
+		}
+		catch ( RuntimeException e ) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	private void handleUndefinedName(ISigilProjectModel project,
+			IInvocationContext context, IProblemLocation problem,
+			HashMap<Object, IJavaCompletionProposal> results) {
+		Name node = findNode(context, problem);
+		
+		if ( node == null ) {
+			return;
+		}
+		addSearchResults(node, project, results);
+	}
+
+	private void handleIsClassPathCorrect(ISigilProjectModel project, final IInvocationContext context,
+			IProblemLocation problemLocation,
+			final HashMap<Object, IJavaCompletionProposal> results) {
+		for ( final String type : problemLocation.getProblemArguments() ) {
+			final String iPackage = type.substring(0, type.lastIndexOf("."));
+			
+			for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) { 
+				results.put( type, new ImportPackageProposal( pe, project ) );
+			}
+		}
+		
+		if ( !results.containsKey(SYNC_FLAG) ) {
+			//results.put( SYNC_FLAG, null);
+		}
+	}
+
+	private void handleUndefinedType(ISigilProjectModel project, IInvocationContext context,
+			IProblemLocation problem,
+			HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+		Name node = findNode(context, problem);
+		
+		if ( node == null ) {
+			return;
+		}
+		addSearchResults(node, project, results);
+	}
+
+	private void handleImportNotFound(ISigilProjectModel project, final IInvocationContext context, IProblemLocation location, final HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+		ASTNode selectedNode= location.getCoveringNode(context.getASTRoot());
+		if (selectedNode == null) return;
+		
+		if ( selectedNode instanceof ClassInstanceCreation ) {
+			ClassInstanceCreation c = (ClassInstanceCreation) selectedNode;
+			Type t = c.getType();
+			Name node = findName( t );
+			if ( node != null ) {
+				addSearchResults(node, project, results);
+			}
+		}
+		else {
+			for ( final String iPackage : readPackage(selectedNode, location) ) {
+				if ( !results.containsKey(iPackage) ) {
+					for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) { 
+						results.put( iPackage, new ImportPackageProposal( pe, project ) );
+					}
+				}
+			}
+		}
+	}
+
+	private void addSearchResults(Name node, ISigilProjectModel project,
+			HashMap<Object, IJavaCompletionProposal> results) {
+		for ( ISearchResult result : SigilSearch.findProviders( node.getFullyQualifiedName(), project, null) ) {
+			if ( project.getBundle().findImport( result.getPackageName() ) == null ) {
+				String type = result.getPackageName() + "." + node.getFullyQualifiedName();
+				results.put( type, new ImportSearchResultProposal( SigilUI.getActiveWorkbenchShell(), result, node, project ) );
+			}
+		}		
+	}
+
+	private Name findName(Type t) {
+		if ( t.isSimpleType() ) {
+			SimpleType st = (SimpleType) t;
+			return st.getName();
+		}
+		else if ( t.isArrayType() ) {
+			ArrayType at = (ArrayType) t;
+			return findName(at.getElementType());
+		}
+		else {
+			return null;
+		}
+	}
+
+	private Name findNode(IInvocationContext context, IProblemLocation problem) {
+		ASTNode selectedNode= problem.getCoveringNode(context.getASTRoot());
+		if (selectedNode == null) {
+			return null;
+		}
+
+		while (selectedNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
+			selectedNode= selectedNode.getParent();
+		}
+
+		Name node= null;
+		
+		if (selectedNode instanceof Type) {
+			node = findName( (Type) selectedNode );
+		} else if (selectedNode instanceof Name) {
+			node= (Name) selectedNode;
+		}
+		
+		return node;
+	}
+
+	private ISigilProjectModel findProject(IInvocationContext context) throws CoreException {
+		IProject project = context.getCompilationUnit().getJavaProject().getProject();
+		if ( project.hasNature( SigilCore.NATURE_ID ) ) {
+			return SigilCore.create(project);
+		}
+		else {
+			return null;
+		}
+	}
+
+	private String[] readPackage(ASTNode selectedNode, IProblemLocation location) {
+		ArrayList<String> packages = new ArrayList<String>();
+		
+		ImportDeclaration id = (ImportDeclaration) ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
+
+		if ( id == null ) {
+			MethodInvocation m = (MethodInvocation) ASTNodes.getParent(selectedNode, ASTNode.METHOD_INVOCATION);
+
+			if (m != null) {
+				packages.add( readPackage( m ) );
+				while ( m.getExpression() != null && m.getExpression() instanceof MethodInvocation) {
+					m = (MethodInvocation) m.getExpression();
+					packages.add( readPackage( m ) );
+				}
+			}
+		}
+		else {
+			if ( id.isOnDemand() ) {
+				packages.add(id.getName().toString());
+			}
+			else {
+				String iStr = id.getName().toString();
+				packages.add(iStr.substring(0, iStr.lastIndexOf( "." ) ));
+			}
+		}
+		
+		return packages.toArray( new String[packages.size()] );
+	}
+
+	private String readPackage(MethodInvocation m) {
+		return m.resolveMethodBinding().getDeclaringClass().getPackage().getName();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
new file mode 100644
index 0000000..fbbf524
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.ui.CodeStyleConfiguration;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportSearchResultProposal implements IJavaCompletionProposal {
+
+	private ISigilProjectModel project;
+	private ICompilationUnit fCompilationUnit;
+	private final ISearchResult result;
+	private final Shell shell;
+	
+	public ImportSearchResultProposal(Shell shell, ISearchResult result, Name node, ISigilProjectModel project) {
+		this.shell = shell;
+		this.result = result;
+		this.project = project;
+		if ( node != null ) {
+			CompilationUnit cu = (CompilationUnit) ASTNodes.getParent(node, ASTNode.COMPILATION_UNIT);
+			this.fCompilationUnit = (ICompilationUnit) cu.getJavaElement();
+		}
+	}
+
+	public int getRelevance() {
+		return 100;
+	}
+
+	public void apply(IDocument document) {
+		IPackageExport e = result.getExport();
+		if ( result.getExport() == null ) {
+			if ( MessageDialog.openQuestion(shell, "Modify " + result.getProvider().getBundleInfo().getSymbolicName(), result.getPackageName() + " is not exported. Do you want to export it now?" ) ) {
+				final IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+				pe.setPackageName(result.getPackageName());
+				//e.setVersion(version)
+				final ISigilProjectModel mod = result.getProvider().getAncestor(ISigilProjectModel.class);
+				if ( mod == null ) {
+					throw new IllegalStateException( "Attempt to modify binary package export" );
+				}
+				WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+					@Override
+					protected void execute(IProgressMonitor monitor)
+					throws CoreException {
+						mod.getBundle().getBundleInfo().addExport(pe);
+						mod.save(null);
+					}
+				};
+
+				SigilUI.runWorkspaceOperation(op, null);
+				e = pe;
+			}
+		}
+
+		final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+		i.setPackageName(e.getPackageName());
+		IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+		VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+		VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+		Version version = e.getVersion();
+		VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+		i.setVersions( selectedVersions );
+
+		WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+			@Override
+			protected void execute(IProgressMonitor monitor)
+			throws CoreException {
+				project.getBundle().getBundleInfo().addImport( i );
+				project.save(null);
+			}
+		};
+
+		SigilUI.runWorkspaceOperation(op, null);
+		addSourceImport();
+	}
+
+	private void addSourceImport() {
+		// add import
+		try {
+			ImportRewrite rewrite= CodeStyleConfiguration.createImportRewrite(fCompilationUnit, true);
+			rewrite.addImport(result.getClassName());
+			JavaModelUtil.applyEdit(fCompilationUnit, rewrite.rewriteImports(null), false, null);
+		} catch (CoreException e) {
+			SigilCore.error( "Failed to add import", e);
+		}
+	}
+
+	public String getAdditionalProposalInfo() {
+		return null;
+	}
+
+	public IContextInformation getContextInformation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+	
+	public String getDisplayString() {
+		String type = result.getClassName();
+		String loc = result.getExport() == null ? " from " + result.getProvider().getBundleInfo().getSymbolicName() : " version " + result.getExport().getVersion();
+		return "Import " + type + loc;
+	}
+
+	public Image getImage() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public Point getSelection(IDocument document) {
+		return null;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
new file mode 100644
index 0000000..3a264c2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class ImportedClassReference {
+	private IPackageImport pi;
+	private String type;
+	
+	public ImportedClassReference(IPackageImport packageImport, String typeName) {
+		this.pi = packageImport;
+		this.type = typeName;
+	}
+
+	public IPackageImport getPackageImport() {
+		return pi;
+	}
+
+	public String getFullType() {
+		return type;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
new file mode 100644
index 0000000..ce5075f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.refactor;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+public class RenameCompositeRefactoring extends Refactoring {
+
+	@Override
+	public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException,
+			OperationCanceledException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+			OperationCanceledException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Change createChange(IProgressMonitor pm) throws CoreException,
+			OperationCanceledException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
new file mode 100644
index 0000000..3ad4fe9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public abstract class AccumulatorAdapter<E> implements IAccumulator<E> {
+	public void addElement(E element) {
+		LinkedList<E> list = new LinkedList<E>();
+		list.add(element);
+		addElements(list);
+	};
+	
+	public void addElements(Collection<? extends E> elements) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
new file mode 100644
index 0000000..8ad02de
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
@@ -0,0 +1,487 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalListener;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class BackgroundLoadingSelectionDialog<E> extends TitleAreaDialog implements IAccumulator<E> {
+
+	private final ILabelProvider DEFAULT_LABEL_PROVIDER = new LabelProvider() {
+		@SuppressWarnings("unchecked")
+		public String getText(Object element) {
+			String result;
+			if(element instanceof WrappedContentProposal<?>) {
+				WrappedContentProposal<E> contentProposal = (WrappedContentProposal<E>) element;
+				result = contentProposal.getLabel();
+			} else {
+				result = descriptor.getLabel((E) element);
+			}
+			return result;
+		}
+	};
+
+	private final IElementDescriptor<E> DEFAULT_DESCRIPTOR = new IElementDescriptor<E>() {
+		public String getLabel(E element) {
+			return getName(element);
+		}
+		public String getName(E element) {
+			return element == null ? "null" : element.toString();
+		}
+	};
+	
+	private final String selectionLabel;
+	private IFilter<? super E> filter;
+	private IElementDescriptor<? super E> descriptor = DEFAULT_DESCRIPTOR;
+	private ILabelProvider labelProvider = DEFAULT_LABEL_PROVIDER;
+	private final boolean multi;
+	
+	private final List<E> elements;
+	
+	private List<E> selection = null;
+	private String selectedName = null;
+	
+	private TableViewer viewer = null;
+	private Comparator<? super E> comparator;
+	
+	private HashMap<String, IJobRunnable> background = new HashMap<String, IJobRunnable>(); 
+	
+	public BackgroundLoadingSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+		super(parentShell);
+		elements = new ArrayList<E>();		
+		this.selectionLabel = selectionLabel;
+		this.multi = multi;
+	}
+	
+	public void setFilter(IFilter<? super E> filter) {
+		this.filter = filter;		
+	}
+	
+	public void setDescriptor(final IElementDescriptor<? super E> descriptor) {
+		if(descriptor != null) {
+			this.descriptor = descriptor;
+		} else {
+			this.descriptor = DEFAULT_DESCRIPTOR;
+		}
+	}
+	
+	public IElementDescriptor<? super E> getDescriptor() {
+		return descriptor;
+	}
+
+	public void setComparator(Comparator<? super E> comparator) {
+		this.comparator = comparator;		
+	}
+	
+	public void setLabelProvider(ILabelProvider labelProvider) {
+		if(labelProvider != null) {
+			this.labelProvider = labelProvider;
+		} else {
+			this.labelProvider = DEFAULT_LABEL_PROVIDER;
+		}
+	}
+	
+	public void addBackgroundJob(String name, IJobRunnable job) {
+		background.put(name, job);
+	}
+	
+	@Override
+	public int open() {
+		Job[] jobs = scheduleJobs();
+		try {
+			return super.open();
+		}
+		finally {
+			for ( Job j : jobs ) {
+				j.cancel();
+			}
+		}
+	}
+
+	private Job[] scheduleJobs() {
+		if ( background.isEmpty() ) {
+			return new Job[] {};
+		}
+		else {
+			ArrayList<Job> jobs = new ArrayList<Job>(background.size()); 
+			for ( Map.Entry<String, IJobRunnable> e : background.entrySet() ) {
+				final IJobRunnable run = e.getValue();
+				Job job = new Job(e.getKey()) {
+					@Override
+					protected IStatus run(IProgressMonitor monitor) {
+						return run.run(monitor);
+					}
+				};				
+				job.schedule();
+			}
+			
+			return jobs.toArray( new Job[jobs.size()] );
+		}
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		// Create Controls
+		Composite container = (Composite) super.createDialogArea(parent);
+		Composite composite = new Composite(container, SWT.NONE);
+		
+		new Label(composite, SWT.NONE).setText(selectionLabel);
+		
+		ContentProposalAdapter proposalAdapter = null;
+		Text txtSelection = null;
+		
+		Table table = null;
+		if(multi) {
+			table = new Table(composite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
+			viewer = new TableViewer(table);
+			viewer.setContentProvider(new ArrayContentProvider());
+			viewer.addFilter(new ViewerFilter() {
+				public boolean select(Viewer viewer, Object parentElement, Object element) {
+					@SuppressWarnings("unchecked") E castedElement = (E) element;
+					return filter == null || filter.select(castedElement);
+				}
+			});
+			if(comparator != null) {
+				viewer.setSorter(new ViewerSorter() {
+					@Override
+					public int compare(Viewer viewer, Object o1, Object o2) {
+						@SuppressWarnings("unchecked")
+						E e1 = (E) o1;
+						@SuppressWarnings("unchecked")
+						E e2 = (E) o2;
+						return comparator.compare(e1, e2);
+					}
+				});
+			}
+			synchronized (elements) {
+				viewer.setInput(elements);
+			}
+			
+			if(labelProvider != null) {
+				viewer.setLabelProvider(labelProvider);
+			}
+		} else {
+			txtSelection = new Text(composite, SWT.BORDER);
+			ControlDecoration selectionDecor = new ControlDecoration(txtSelection, SWT.LEFT | SWT.TOP);
+			FieldDecoration proposalDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
+			selectionDecor.setImage(proposalDecor.getImage());
+			selectionDecor.setDescriptionText(proposalDecor.getDescription());
+			
+			ExclusionContentProposalProvider<E> proposalProvider = new ExclusionContentProposalProvider<E>(elements, filter, descriptor);
+			
+			proposalAdapter = new ContentProposalAdapter(txtSelection, new TextContentAdapter(), proposalProvider, null, null);
+			proposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+			if(labelProvider != null) {
+				proposalAdapter.setLabelProvider(labelProvider);
+			}
+			
+			if(selectedName != null) {
+				txtSelection.setText(selectedName);
+			}
+		}
+		updateSelection();
+		updateButtons();
+		
+		// Hookup listeners
+		if(proposalAdapter != null) {
+			proposalAdapter.addContentProposalListener(new IContentProposalListener() {
+				public void proposalAccepted(IContentProposal proposal) {
+					@SuppressWarnings("unchecked")
+					WrappedContentProposal<E> valueProposal = (WrappedContentProposal<E>) proposal;
+					E selected = valueProposal.getElement();
+					selection = new ArrayList<E>(1);
+					selection.add(selected);
+					
+					elementSelected(selected);
+					
+					updateButtons();
+				}
+			});
+		}
+		if(txtSelection != null) {
+			txtSelection.addModifyListener(new ModifyListener() {
+				public void modifyText(ModifyEvent e) {
+					selectedName = ((Text) e.widget).getText();
+					updateButtons();
+				}
+			});
+		}
+		if(viewer != null) {
+			viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+				public void selectionChanged(SelectionChangedEvent event) {
+					IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+					selection = new ArrayList<E>(sel.size());
+					for(Iterator<?> iter = sel.iterator(); iter.hasNext(); ) {
+						@SuppressWarnings("unchecked")
+						E element = (E) iter.next();
+						selection.add(element);
+					}
+					updateButtons();
+				}
+			});
+			viewer.addOpenListener(new IOpenListener() {
+				public void open(OpenEvent event) {
+					if(canComplete()) {
+						setReturnCode(IDialogConstants.OK_ID);
+						close();
+					}
+				}
+			});
+		}
+		
+		// Layout
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		if(multi) {
+			composite.setLayout(new GridLayout(1, false));
+			GridData layoutTable = new GridData(SWT.FILL, SWT.FILL, true, true);
+			layoutTable.heightHint = 200;
+			table.setLayoutData(layoutTable);
+		} else {
+			composite.setLayout(new GridLayout(2, false));
+			txtSelection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		}
+
+		return container;
+	}
+	
+	protected void elementSelected(E selection) {
+	}
+	
+	@Override
+	protected Control createButtonBar(Composite parent) {
+		Control bar = super.createButtonBar(parent);
+		updateButtons();
+		return bar;
+	}
+
+	/**
+	 * Can be called from any thread
+	 */
+	protected final void updateButtons() {
+		Runnable updateButtonsRunnable = new Runnable() {
+			public void run() {
+				Shell shell = getShell();
+				if(shell != null && !shell.isDisposed()) {
+					Button okButton = getButton(IDialogConstants.OK_ID);
+					if(okButton != null && !okButton.isDisposed()) {
+						okButton.setEnabled(canComplete());
+					}
+				}
+			}
+		};
+		Shell shell = getShell();
+		if (shell != null) {
+			onUIThread(shell, updateButtonsRunnable);
+		}
+	}
+	
+	/**
+	 * Subclasses may override but must call super.canComplete
+	 * @return
+	 */
+	protected synchronized boolean canComplete() {
+		boolean result = false;
+		
+		if ( selection != null ) {
+			if(multi) {
+				result = selection.size() > 0;
+			} else {
+				E sel = getSelectedElement();
+				result = sel != null && descriptor.getName(sel).equals(selectedName);
+			}
+		}
+		
+		return result;
+	}
+	
+	public final void addElement(E added) {
+		addElements(Collections.singleton(added));
+	}
+	
+	/**
+	 * Can be called from any thread
+	 */
+	public final void addElements(Collection<? extends E> added) {
+		final LinkedList<E> toAdd = new LinkedList<E>();
+		synchronized (elements) {
+			for ( E e : added ) {
+				if ( !elements.contains(e) ) {
+					elements.add( e );
+					toAdd.add(e);
+				}
+			}
+			Collections.sort(elements, comparator);
+		}
+		if(viewer != null) {
+			onUIThread(viewer.getControl(), new Runnable() {
+				public void run() {
+					if(!viewer.getControl().isDisposed()) {
+						viewer.add( toAdd.toArray() );
+						viewer.refresh();
+					}
+				}
+			});
+		}
+		else {
+			
+		}
+		updateSelection();
+		updateButtons();
+	}
+
+	protected void updateSelection() {
+		onUIThread(getShell(), new Runnable() {
+			public void run() {
+				if(selectedName != null) {
+					ArrayList<E> newSelection = new ArrayList<E>();
+					synchronized (elements) {
+						for (E e : elements) {
+							if(selectedName.equals(descriptor.getName(e))) {
+								newSelection.add(e);
+								break;
+							}
+						}
+					}
+					selection = newSelection;
+				}
+				else {
+					selection = Collections.emptyList();
+				}
+				if(viewer != null && !viewer.getControl().isDisposed()) {
+					viewer.setSelection(selection.isEmpty() ? StructuredSelection.EMPTY : new StructuredSelection(selection));
+				}
+			}
+		});			
+	}
+
+	private static final void onUIThread(Control control, Runnable r) {
+		if(control != null && !control.isDisposed()) {
+			try {
+				Display display = control.getDisplay();
+				if(Thread.currentThread() == display.getThread()) {
+					// We are on the UI thread already, just do the work
+					r.run();
+				} else {
+					// Not on the UI thread, need to bung over the runnable
+					display.asyncExec(r);
+				}
+			}
+			catch (SWTError e) {
+				if ( e.code == SWT.ERROR_WIDGET_DISPOSED ) {
+					// ignore
+				}
+				else {
+					throw e;
+				}
+			}
+		}
+	}
+	
+	public String getSelectedName() {
+		return selectedName;
+	}
+	
+	public void setSelectedName(String selectedName) {
+		this.selectedName = selectedName;
+		boolean change = false;
+		if ( selectedName == null ) {
+			if ( selection != null && !selection.isEmpty() ) {
+				change = true;
+			}
+		}
+		else {
+			if ( selection == null ) {
+				change = true;
+			}
+			else if ( selection.size() != 1 || !descriptor.getLabel(selection.get(0)).equals(selectedName)) {
+				change = true;				
+			}
+		}
+		
+		if ( change ) {
+			updateSelection();
+			updateButtons();
+		}
+	}
+	
+	public List<E> getSelectedElements() {
+		return selection;
+	}
+	
+	public E getSelectedElement() {
+		E result;
+		if(selection == null || selection.isEmpty()) {
+			result = null;
+		} else {
+			result = selection.get(0);
+		}
+		return result;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
new file mode 100644
index 0000000..2febfbd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class ColumnModelLabelProvider extends ColumnLabelProvider {
+
+	private ModelLabelProvider provider = new ModelLabelProvider();
+	
+	@Override
+	public Image getImage(Object element) {
+		return provider.getImage(element);
+	}
+
+	@Override
+	public String getText(Object element) {
+		return provider.getText(element);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
new file mode 100644
index 0000000..5fb4237
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class DefaultContentProvider implements IContentProvider {
+
+	public void dispose() {
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
new file mode 100644
index 0000000..29fb24b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+
+public abstract class DefaultLabelProvider implements IBaseLabelProvider, ILabelProvider {
+
+	public boolean isLabelProperty(Object element, String property) {
+		return false;
+	}
+
+	public void dispose() {
+	}
+
+	public void addListener(ILabelProviderListener listener) {
+	}
+
+	public void removeListener(ILabelProviderListener listener) {
+	}
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
new file mode 100644
index 0000000..715afa9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+
+public abstract class DefaultTableProvider extends DefaultContentProvider implements IStructuredContentProvider {
+
+	/**
+	 * Utility method to convert the input element to an Object[].
+	 * 
+	 * @param inputElement
+	 * 
+	 * @return if inputElement is null -> empty array <br/>
+	 *         if inputElement is a {@link Collection} returns {@link Collection#toArray()}<br/>
+	 *         if inputElement is an Array class cast of inputElement to Object[]<br/>
+	 *  
+	 * @throws IllegalArgumentException if the element cannot be converted. 
+	 */
+	public Object[] toArray(Object inputElement) {
+		if ( inputElement == null ) {
+			return new Object[] {};
+		}
+		else if ( inputElement instanceof Collection ) {
+			Collection<?> col = (Collection<?>) inputElement;
+			return col.toArray();
+		}
+		else if ( inputElement.getClass().isArray() ) {
+			return (Object[]) inputElement;
+		}
+		else {
+			throw new IllegalArgumentException( "Invalid inputElement " + inputElement.getClass() );
+		}		
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
new file mode 100644
index 0000000..d223835
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+
+public abstract class DefaultTreeContentProvider extends DefaultContentProvider implements ITreeContentProvider {
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
new file mode 100644
index 0000000..1663e2f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+public class ExclusionContentProposalProvider<T> implements
+		IContentProposalProvider {
+	
+	private final Collection<? extends T> elements;
+	private final IFilter<? super T> filter;
+	private final IElementDescriptor<? super T> descriptor;
+
+	public ExclusionContentProposalProvider(Collection<? extends T> elements, IFilter<? super T> filter, IElementDescriptor<? super T> descriptor) {
+		this.elements = elements;
+		this.filter = filter;
+		this.descriptor = descriptor;
+	}
+	
+	public IContentProposal[] getProposals(String contents, int position) {
+		String matchString = contents.substring(0, position);
+		Pattern pattern = GlobCompiler.compile(matchString);
+
+		// Get a snapshot of the elements
+		T[] elementArray;
+		synchronized (elements) {
+			@SuppressWarnings("unchecked") T[] temp = (T[]) elements.toArray();
+			elementArray = temp;
+		}
+		
+		List<IContentProposal> result = new ArrayList<IContentProposal>();
+		
+		for (T element : elementArray) {
+			if(filter != null && filter.select(element)) {
+				IContentProposal proposal = WrappedContentProposal.newInstance(element, descriptor);
+				Matcher matcher = pattern.matcher(proposal.getContent());
+				if(matcher.find()) {
+					result.add(proposal);
+				}
+			}
+		}
+		
+		return result.toArray(new IContentProposal[result.size()]);
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
new file mode 100644
index 0000000..5c85724
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ExportedPackageFinder implements IJobRunnable {
+	
+	private final IAccumulator<? super IPackageExport> accumulator;
+	private final ISigilProjectModel sigil;
+
+	public ExportedPackageFinder(ISigilProjectModel sigil, IAccumulator<? super IPackageExport> accumulator) {
+		this.sigil = sigil;
+		this.accumulator = accumulator;
+	}
+
+	public IStatus run(final IProgressMonitor monitor) {
+		final List<IPackageExport> exports = new ArrayList<IPackageExport>(ResourcesDialogHelper.UPDATE_BATCH_SIZE);
+		final IModelWalker walker = new IModelWalker() {
+			public boolean visit(IModelElement element) {
+				if ( element instanceof IPackageExport ) {
+					IPackageExport pkgExport = (IPackageExport) element;
+					exports.add(pkgExport);
+
+					if(exports.size() >= ResourcesDialogHelper.UPDATE_BATCH_SIZE) {
+						accumulator.addElements(exports);
+						exports.clear();
+					}
+				}
+				return !monitor.isCanceled();
+			}
+		};
+		SigilCore.getRepositoryManager(sigil).visit(walker);
+		if(exports.size() > 0) {
+			accumulator.addElements(exports);
+		}
+
+		return Status.OK_STATUS;
+	}
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
new file mode 100644
index 0000000..60cb121
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;

+

+import org.eclipse.swt.SWT;

+import org.eclipse.swt.widgets.DirectoryDialog;

+import org.eclipse.swt.widgets.FileDialog;

+import org.eclipse.swt.widgets.Shell;

+import org.eclipse.swt.widgets.Text;

+

+public class FileUtils {

+	public static void loadFile( Shell shell, Text text, String msg, boolean isDirectory ) {

+		if ( isDirectory ) {

+			DirectoryDialog dialog = new DirectoryDialog(shell, SWT.NONE);

+			dialog.setMessage(msg);

+			String value = dialog.open();

+			if ( value != null ) {

+				text.setText( value );

+			}

+		}

+		else {

+			FileDialog dialog = new FileDialog(shell, SWT.NONE);

+			dialog.setText(msg);

+			String value = dialog.open();

+			if ( value != null ) {

+				text.setText( value );

+			}

+		}

+	}

+

+}

diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
new file mode 100644
index 0000000..99384be
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+public interface IAccumulator<E> {
+	public void addElement(E element);
+	public void addElements(Collection<? extends E> elements);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
new file mode 100644
index 0000000..915d16c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IExportToImportConverter<E,I> {
+	I convert(E exportElement);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
new file mode 100644
index 0000000..6cd3f23
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IFilter<T> {
+	boolean select(T element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
new file mode 100644
index 0000000..6c1b701
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IValidationListener {
+
+	void validationMessage(String message, int level);
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
new file mode 100644
index 0000000..6ff6901
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Version;
+
+public class ModelLabelProvider extends LabelProvider {
+	
+	private volatile Set<? extends IModelElement> unresolvedElements = null;
+	
+	public Image getImage(Object element) {
+		boolean unresolved = (unresolvedElements == null) ? false : unresolvedElements.contains(element);
+		
+		if ( element instanceof ISigilBundle || element instanceof IBundleModelElement) {
+			return findBundle();
+		} else if(element instanceof IRequiredBundle) {
+			boolean optional = ((IRequiredBundle) element).isOptional();
+			return findRequiredBundle(optional, unresolved);
+		}
+		else if ( element instanceof IPackageImport ) {
+			boolean optional = ((IPackageImport) element).isOptional();
+			return findPackageImport(optional, unresolved);
+		}
+		else if ( element instanceof IPackageExport ) {
+			return findPackageExport();
+		}
+		else if ( element instanceof IPackageFragmentRoot ) {
+			IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+			try {
+				if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+					return findPackage();
+				}
+				else {
+					return findBundle();
+				}
+			} catch (JavaModelException e) {
+				SigilCore.error( "Failed to inspect package fragment root", e );
+			}
+		}
+		else if ( element instanceof IClasspathEntry ) {
+			return findPackage();
+		}
+		if ( element instanceof IBundleRepository ) {
+			IBundleRepository rep = (IBundleRepository) element;
+			IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+			return config.getType().getIcon();
+		}
+	
+		return null;
+	}
+
+	public String getText(Object element) {
+		if ( element instanceof ISigilBundle ) {
+			ISigilBundle bundle = (ISigilBundle) element;
+			return bundle.getBundleInfo().getSymbolicName() + " " + bundle.getBundleInfo().getVersion(); 
+		}
+		if ( element instanceof IBundleModelElement ) {
+			IBundleModelElement bundle = (IBundleModelElement) element;
+			return bundle.getSymbolicName();
+		}
+		if ( element instanceof IRequiredBundle ) {
+			IRequiredBundle req = (IRequiredBundle) element;
+			return req.getSymbolicName() + " " + req.getVersions();
+		}
+		
+		if ( element instanceof IPackageImport ) {
+			IPackageImport req = (IPackageImport) element;
+			return req.getPackageName() + " " + req.getVersions();
+		}
+		
+		if ( element instanceof IPackageExport ) {
+			IPackageExport pe = (IPackageExport) element;
+			Version rawVersion = pe.getRawVersion();
+			return rawVersion != null ? pe.getPackageName() + " " + rawVersion : pe.getPackageName();
+		}
+		
+		if ( element instanceof IResource ) {
+			IResource resource = (IResource) element;
+			return resource.getName();
+		}
+		
+		if ( element instanceof IPackageFragment )  {
+			IPackageFragment f = (IPackageFragment) element;
+			return f.getElementName();
+		}
+		
+		if ( element instanceof IPackageFragmentRoot ) {
+			IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+			try {
+				return f.getUnderlyingResource().getName();
+			} catch (JavaModelException e) {
+				return "unknown";
+			}
+		}
+		
+		if ( element instanceof IClasspathEntry ) {
+			IClasspathEntry cp = (IClasspathEntry) element;
+			return cp.getPath().toString();
+		}
+		
+		if ( element instanceof IBundleRepository ) {
+			IBundleRepository rep = (IBundleRepository) element;
+			IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+			return config.getName();
+		}
+		
+		return element.toString();
+	}
+
+	private Image findPackage() {
+		return cacheImage( "icons/package_obj.png" ); 
+	}
+
+	private Image findPackageImport(boolean optional, boolean unresolved) {
+		String path;
+		if(optional) {
+			path = unresolved ? "icons/package_obj_import_opt_error.png" : "icons/package_obj_import_opt.png";
+		} else {
+			path = unresolved ? "icons/package_obj_import_error.png" : "icons/package_obj_import.png";
+		}
+		return cacheImage(path);
+	}
+	
+	private Image findPackageExport() {
+		return cacheImage("icons/package_obj_export.png");
+	}
+	
+	private Image findBundle() {
+		return cacheImage("icons/jar_obj.png");
+	}
+	
+	private Image findRequiredBundle(boolean optional, boolean unresolved) {
+		String path;
+		if(optional) {
+			path = unresolved ? "icons/required_bundle_opt_error.png" : "icons/required_bundle_opt.png";
+		} else {
+			path = unresolved ? "icons/required_bundle_error.png" : "icons/jar_obj.png";
+		}
+		return cacheImage(path);
+	}
+	
+	private static Image cacheImage(String path) {
+		return SigilUI.cacheImage(path, ModelLabelProvider.class.getClassLoader());
+	}
+
+	public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+		this.unresolvedElements = elements;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
new file mode 100644
index 0000000..d451538
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class PackageFilter implements IFilter<IPackageImport> {
+	
+	private Set<String> names = new HashSet<String>();
+	
+	public PackageFilter(String[] packageNames) {
+		for (String name : packageNames) {
+			names.add(name);
+		}
+	}
+	
+	public PackageFilter(IPackageExport[] packages) {
+		for (IPackageExport packageExport : packages) {
+			names.add(packageExport.getPackageName());
+		}
+	}
+
+	public boolean select(IPackageImport element) {
+		return !names.contains(element.getPackageName());
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
new file mode 100644
index 0000000..4cf8ad4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ProjectUtils {
+	public static boolean runTaskWithRebuildCheck(final Runnable task, Shell shell) {
+		return runTaskWithRebuildCheck( new Callable<Boolean>() {
+			public Boolean call() throws Exception {
+				task.run();
+				return true;
+			}
+		}, shell);
+	}
+	
+	public static boolean runTaskWithRebuildCheck(Callable<Boolean> callable,
+			Shell shell) {
+		int result = checkRebuild(shell);
+		if ( result == IDialogConstants.CANCEL_ID ) {
+			return false;
+		}
+		else {
+			try {
+				if ( Boolean.TRUE == callable.call() ) {
+					if ( result == IDialogConstants.YES_ID ) {
+						SigilUI.runWorkspaceOperation( new WorkspaceModifyOperation() {
+							@Override
+							protected void execute(IProgressMonitor monitor) {
+								SigilCore.rebuildAllBundleDependencies(monitor);
+							}
+						}, shell );
+					}
+					return true;
+				}
+				else {
+					return false;
+				}
+			} catch (Exception e) {
+				SigilCore.error( "Failed to run caller", e);
+				return false;
+			}
+		}
+	}
+	
+	private static int checkRebuild(Shell shell) {
+		if ( SigilCore.getRoot().getProjects().isEmpty() ) {
+			return IDialogConstants.NO_ID;
+		}
+		else {
+			return OptionalPrompt.optionallyPromptWithCancel(SigilCore.getDefault().getPreferenceStore(), SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild", "Do you wish to rebuild all Sigil projects", shell );
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
new file mode 100644
index 0000000..bebe867
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class ResourceReviewDialog<T extends IModelElement> extends TitleAreaDialog {
+
+	private String title;	
+	private Collection<T> resources;
+	
+	private TableViewer viewer;
+
+	public ResourceReviewDialog(Shell parentShell, String title, Collection<T> resources) {
+		super(parentShell);
+		this.title = title;
+		this.resources = resources;
+	}
+
+	public Collection<T> getResources() {
+		return resources;
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		setTitle(title);
+
+		// Create controls
+		Composite container = (Composite) super.createDialogArea(parent);
+		Composite composite = new Composite(container, SWT.NONE);		
+		Table table = new Table(composite, SWT.BORDER | SWT.VIRTUAL);
+		
+		final Button remove = new Button(composite, SWT.PUSH);
+		remove.setText("Remove");
+		remove.setEnabled(false);
+		
+		remove.addSelectionListener( new SelectionAdapter() {
+			@Override
+			public void widgetSelected(SelectionEvent e) {
+				handleRemove();
+			}
+		} );
+		
+		viewer = new TableViewer(table);
+		viewer.setContentProvider( new DefaultTableProvider() {
+			public Object[] getElements(Object inputElement) {
+				return toArray(inputElement);
+			}
+		});
+		
+		viewer.setInput( resources );
+		viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+			public void selectionChanged(SelectionChangedEvent event) {
+				remove.setEnabled(!event.getSelection().isEmpty());
+			}
+		});
+		
+		// layout
+		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+		composite.setLayout(new GridLayout(2, false));
+		GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4);
+		tableLayoutData.heightHint = 150;
+		table.setLayoutData(tableLayoutData);
+		
+		return container;
+	}
+
+	private void handleRemove() {
+		ISelection s = viewer.getSelection();
+		if ( !s.isEmpty() ) {
+			IStructuredSelection sel = (IStructuredSelection) s;
+			resources.remove( sel.getFirstElement() );
+			viewer.refresh();
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
new file mode 100644
index 0000000..4644fe3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.NewPackageExportDialog;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ResourcesDialogHelper {
+	
+	static final int UPDATE_BATCH_SIZE = 100;
+
+	public static BackgroundLoadingSelectionDialog<String> createClassSelectDialog(Shell shell, String title, final ISigilProjectModel project, String selected, final String ifaceOrParentClass) {
+		final BackgroundLoadingSelectionDialog<String> dialog = new BackgroundLoadingSelectionDialog<String>(shell, "Class Name", true);
+		
+		IJobRunnable job = new IJobRunnable() {
+			public IStatus run(IProgressMonitor monitor) {
+				try {
+					for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT ) ) {
+						IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+						if ( project.isInBundleClasspath(root)) {
+							for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.COMPILATION_UNIT, IJavaElement.CLASS_FILE ) ) {
+								ITypeRoot typeRoot = (ITypeRoot) e1;
+								IType type = (IType) JavaHelper.findType(typeRoot, IJavaElement.TYPE);
+								if ( JavaHelper.isAssignableTo( ifaceOrParentClass, type ) ) {
+									dialog.addElement(type.getFullyQualifiedName());					
+								}							
+							}
+						}
+					}
+					
+					return Status.OK_STATUS;
+				} catch (JavaModelException e) {
+					return e.getStatus();
+				}				
+			}
+
+		};
+		
+		dialog.addBackgroundJob("Scanning for activators in project", job);
+
+		return dialog;
+	}
+	
+	public static NewResourceSelectionDialog<IPackageExport> createImportDialog(
+			Shell shell, 
+			String title, 
+			ISigilProjectModel sigil, 
+			final IPackageImport selected, 
+			final Collection<IPackageImport> existing) {
+		final Set<String> existingNames = new HashSet<String>();
+		
+		for (IPackageImport existingImport : existing) {
+			existingNames.add(existingImport.getPackageName());
+		}
+		
+		final NewResourceSelectionDialog<IPackageExport> dialog = new NewResourceSelectionDialog<IPackageExport>(shell, "Package Name:", false);
+		
+		dialog.setFilter( new IFilter<IPackageModelElement>() {
+			public boolean select(IPackageModelElement element) {
+				return !existingNames.contains(element.getPackageName());
+			}
+		} );
+		
+		dialog.setComparator(new Comparator<IPackageExport>() {
+			public int compare(IPackageExport o1, IPackageExport o2) {
+				return o1.compareTo(o2);
+			}
+		});
+		
+		dialog.setDescriptor(new IElementDescriptor<IPackageExport>() {
+			public String getLabel(IPackageExport element) {
+				return getName(element) + " (" + element.getVersion().toString() + ")";
+			}
+
+			public String getName(IPackageExport element) {
+				return element.getPackageName();
+			}
+		});
+		
+		dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IPackageExport>(dialog.getDescriptor()));
+		
+		if(selected != null) {
+			dialog.setSelectedName(selected.getPackageName());
+			dialog.setVersions(selected.getVersions());
+			dialog.setOptional(selected.isOptional());
+		}
+
+		IJobRunnable job = new ExportedPackageFinder(sigil, dialog);
+		dialog.addBackgroundJob("Scanning for exports in workspace", job);
+		
+		return dialog;
+	}
+	
+	public static NewPackageExportDialog createNewExportDialog(Shell shell, String title, final IPackageExport selected, final ISigilProjectModel project, boolean multiSelect) {
+		IFilter<IJavaElement> selectFilter = new IFilter<IJavaElement>() {
+			public boolean select(IJavaElement e) {
+				if ( selected != null && e.getElementName().equals( selected.getPackageName() ) ) {
+					return true;
+				}
+				
+				if ( e.getElementName().trim().length() > 0 && isLocal( e ) ) {
+					for ( IPackageExport p : project.getBundle().getBundleInfo().getExports() ) {
+						if ( p.getPackageName().equals( e.getElementName() ) ) {
+							return false;
+						}
+					}
+					
+					return true;
+				}
+				else {
+					return false;
+				}
+			}
+			
+			private boolean isLocal(IJavaElement java) {
+				try {
+					switch ( java.getElementType() ) {
+					case IJavaElement.PACKAGE_FRAGMENT:
+						IPackageFragment fragment= (IPackageFragment) java;
+						return fragment.containsJavaResources();
+					default:
+						throw new IllegalStateException( "Unexpected resource type " + java );
+					}
+				}
+				catch (JavaModelException e) {
+					SigilCore.error( "Failed to inspect java element ", e );
+					return false;
+				}
+			}
+			
+		};
+		
+		final NewPackageExportDialog dialog = new NewPackageExportDialog(shell, multiSelect);
+		dialog.setFilter(selectFilter);
+		
+		dialog.setProjectVersion(project.getVersion());
+		if ( selected != null ) {
+			dialog.setSelectedName(selected.getPackageName());
+			dialog.setVersion(selected.getRawVersion());
+		}
+		
+		IJobRunnable job = new IJobRunnable() {
+			public IStatus run(IProgressMonitor monitor) {
+				try {
+					ArrayList<IPackageFragment> list = new ArrayList<IPackageFragment>(UPDATE_BATCH_SIZE);
+					for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT) ) {
+						IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+						if ( project.isInBundleClasspath(root) ) {
+							for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.PACKAGE_FRAGMENT) ) {
+								list.add((IPackageFragment) e1);
+								if(list.size() >= UPDATE_BATCH_SIZE) {
+									dialog.addElements(list);
+									list.clear();
+								}
+							}
+						}
+					}
+					if(!list.isEmpty()) {
+						dialog.addElements(list);
+					}
+					return Status.OK_STATUS;
+				} catch (JavaModelException e) {
+					return e.getStatus();
+				}
+			}
+		};
+		
+		dialog.addBackgroundJob("Scanning for packages in project", job);
+		
+		return dialog;
+	}
+	
+	public static NewResourceSelectionDialog<IBundleModelElement> createRequiredBundleDialog(Shell shell, String title, final ISigilProjectModel sigil, final IRequiredBundle selected, final Collection<IRequiredBundle> existing) {
+		final Set<String> existingNames = new HashSet<String>();
+		for(IRequiredBundle existingBundle : existing) {
+			existingNames.add(existingBundle.getSymbolicName());
+		}
+		
+		final NewResourceSelectionDialog<IBundleModelElement> dialog = new NewResourceSelectionDialog<IBundleModelElement>(shell, "Bundle:", false);
+		
+		dialog.setDescriptor(new IElementDescriptor<IBundleModelElement>() {
+			public String getLabel(IBundleModelElement element) {
+				return getName(element) + " (" + element.getVersion() + ")";
+			}
+			public String getName(IBundleModelElement element) {
+				return element.getSymbolicName();
+			}
+		});
+		
+		dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IBundleModelElement>(dialog.getDescriptor()));
+		
+		dialog.setFilter(new IFilter<IBundleModelElement>() {
+			public boolean select(IBundleModelElement element) {
+				return !existingNames.contains(element.getSymbolicName());
+			}
+		});
+		
+		dialog.setComparator(new Comparator<IBundleModelElement>() {
+			public int compare(IBundleModelElement o1, IBundleModelElement o2) {
+				return o1.getSymbolicName().compareTo(o2.getSymbolicName());
+			}
+		});
+		
+		if(selected != null) {
+			dialog.setSelectedName(selected.getSymbolicName());
+			dialog.setVersions(selected.getVersions());
+			dialog.setOptional(selected.isOptional());
+		}
+				
+		IJobRunnable job = new IJobRunnable() {
+			public IStatus run(final IProgressMonitor monitor) {
+				final List<IBundleModelElement> bundles = new ArrayList<IBundleModelElement>(UPDATE_BATCH_SIZE);
+				final IModelWalker walker = new IModelWalker() {
+					//int count = 0;
+					public boolean visit(IModelElement element) {
+						if ( element instanceof  IBundleModelElement) {
+							IBundleModelElement b = (IBundleModelElement) element;
+							bundles.add(b);
+
+							if(bundles.size() >= UPDATE_BATCH_SIZE) {
+								dialog.addElements(bundles);
+								bundles.clear();
+							}
+							// no need to recurse further.
+							return false;
+						}
+						return !monitor.isCanceled();
+					}
+				};
+				SigilCore.getRepositoryManager(sigil).visit(walker);
+				if(!bundles.isEmpty()) {
+					dialog.addElements(bundles);
+				}
+				return Status.OK_STATUS;
+			}
+		};
+		
+		dialog.addBackgroundJob("Scanning for bundles in workspace", job);
+		
+		return dialog;
+	}
+}
+
+
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
new file mode 100644
index 0000000..22a0a46
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+@SuppressWarnings("unchecked")
+public class SingletonSelection implements IStructuredSelection {
+
+	private final Object singleton;
+	
+	public SingletonSelection(Object singleton) {
+		this.singleton = singleton;
+	}
+
+	public Object getFirstElement() {
+		return singleton;
+	}
+
+	public Iterator iterator() {
+		return Collections.singleton(singleton).iterator();
+	}
+
+	public int size() {
+		return 1;
+	}
+
+	public Object[] toArray() {
+		return new Object[] { singleton };
+	}
+
+	public List toList() {
+		ArrayList list = new ArrayList(1);
+		list.add( singleton );
+		return list;
+	}
+
+	public boolean isEmpty() {
+		return false;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
new file mode 100644
index 0000000..f46b4b2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class WrappedContentProposalLabelProvider<E> extends LabelProvider {
+	
+	private final IElementDescriptor<? super E> descriptor;
+	private final ModelLabelProvider projectLabelProvider;
+
+	public WrappedContentProposalLabelProvider(IElementDescriptor<? super E> descriptor) {
+		this.descriptor = descriptor;
+		projectLabelProvider = new ModelLabelProvider();
+	}
+	
+	@SuppressWarnings("unchecked")
+	private E adapt(Object element) {
+		E result;
+		if(element instanceof WrappedContentProposal<?>) {
+			WrappedContentProposal<?> proposal = (WrappedContentProposal<?>) element;
+			result = (E) proposal.getElement();
+		} else {
+			result = (E) element;
+		}
+		return result;
+	}
+	
+	@Override
+	public Image getImage(Object element) {
+		Object value = adapt(element);
+		return projectLabelProvider.getImage(value);
+	}
+	
+	@Override
+	public String getText(Object element) {
+		return descriptor.getLabel(adapt(element));
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
new file mode 100644
index 0000000..cbfbbbe
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+public class ModelElementComparator extends ViewerComparator {
+	private static final int EXPORT_GROUP = 0;
+	private static final int IMPORT_GROUP = 1;
+	private static final int REQUIRE_GROUP = 2;
+	private static final int OTHER_GROUP = 4;
+	public int category(Object element) {
+		if ( element instanceof IPackageImport ) {
+			return IMPORT_GROUP;
+		}
+		else if ( element instanceof IPackageExport ) {
+			return EXPORT_GROUP;
+		}
+		else if ( element instanceof IRequiredBundle ) {
+			return REQUIRE_GROUP;
+		}
+		else {
+			return OTHER_GROUP;
+		}
+	}
+	@SuppressWarnings("unchecked")
+	@Override
+	public int compare(Viewer viewer, Object e1, Object e2) {
+        int cat1 = category(e1);
+        int cat2 = category(e2);
+
+        if (cat1 != cat2) {
+			return cat1 - cat2;
+		}
+        
+        if ( cat1 == OTHER_GROUP ) {
+            String name1;
+            String name2;
+
+            if (viewer == null || !(viewer instanceof ContentViewer)) {
+                name1 = e1.toString();
+                name2 = e2.toString();
+            } else {
+                IBaseLabelProvider prov = ((ContentViewer) viewer)
+                        .getLabelProvider();
+                if (prov instanceof ILabelProvider) {
+                    ILabelProvider lprov = (ILabelProvider) prov;
+                    name1 = lprov.getText(e1);
+                    name2 = lprov.getText(e2);
+                } else {
+                    name1 = e1.toString();
+                    name2 = e2.toString();
+                }
+            }
+            if (name1 == null) {
+    			name1 = "";//$NON-NLS-1$
+    		}
+            if (name2 == null) {
+    			name2 = "";//$NON-NLS-1$
+    		}
+
+            // use the comparator to compare the strings
+            return getComparator().compare(name1, name2);
+        }
+        else {
+        	Comparable c1 = (Comparable) e1;
+        	Comparable c2 = (Comparable) e2;
+        	return c1.compareTo(c2);
+        }
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
new file mode 100644
index 0000000..4fdb238
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.util.ModelHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.LocalSelectionTransfer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSourceAdapter;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.part.ViewPart;
+
+public class RepositoryViewPart extends ViewPart implements IRepositoryChangeListener {
+
+	public class FindUsersAction extends Action {
+		@Override
+		public String getText() {
+			return "Find Uses";
+		}
+
+		@Override
+		public void run() {
+			ISelection s = treeViewer.getSelection();
+			if ( !s.isEmpty() ) {
+				IStructuredSelection sel = (IStructuredSelection) s;
+				IModelElement e = (IModelElement) sel.getFirstElement();
+				List<IModelElement>	users = ModelHelper.findUsers(e);
+				String msg = null;
+				if ( users.isEmpty() ) {
+					msg = "No users of " + e;
+				}
+				else {
+					StringBuilder b = new StringBuilder();	
+					for ( IModelElement u : users ) {
+						ISigilBundle bndl = u.getAncestor(ISigilBundle.class);
+						b.append( bndl );
+						b.append( "->" );
+						b.append( u );
+						b.append( "\n" );
+					}
+					msg = b.toString();
+				}
+				MessageDialog.openInformation(getViewSite().getShell(), "Information", msg );
+			}
+		}
+	}
+
+	class RepositoryAction extends Action {
+		final IBundleRepository rep;
+		final IRepositoryModel model;
+		
+		public RepositoryAction(IBundleRepository rep) {
+			this.rep = rep;
+			this.model = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+		}
+		
+		
+		@Override
+		public void run() {
+			treeViewer.setInput(rep);
+			createMenu();
+		}
+
+		@Override
+		public String getText() {
+			String name = model.getName(); 
+			if ( treeViewer.getInput() == rep ) {
+				name = "> " + name;
+			}
+			return name;
+		}
+		
+		@Override
+		public ImageDescriptor getImageDescriptor() {
+			Image img = model.getType().getIcon();
+			if ( img == null ) {
+				return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/jars_obj.png");
+			}
+			else {
+				return ImageDescriptor.createFromImage(img);
+			}
+		}
+	}
+	
+	class RefreshAction extends Action {
+		@Override
+		public void run() {
+			IBundleRepository rep = (IBundleRepository) treeViewer.getInput();
+			if ( rep != null ) {
+				rep.refresh();
+				treeViewer.refresh();
+			}
+		}
+
+		@Override
+		public String getText() {
+			return "Refresh";
+		}
+		
+		@Override
+		public ImageDescriptor getImageDescriptor() {
+			return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/refreshBundle.png");
+		}
+
+	}
+
+	private TreeViewer treeViewer;
+
+	@Override
+	public void createPartControl(Composite parent) {
+		createBody(parent);
+		createMenu();
+		SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(this);
+	}
+
+	@Override
+	public void dispose() {
+		SigilCore.getGlobalRepositoryManager().removeRepositoryChangeListener(this);
+		super.dispose();
+	}
+
+	private void createMenu() {
+		createTopMenu();
+		createLocalMenu();
+	}
+
+	private void createLocalMenu() {
+		/*MenuManager menuMgr = new MenuManager();
+		menuMgr.add( new FindUsersAction() );
+		Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
+
+		treeViewer.getControl().setMenu(menu);
+		getViewSite().registerContextMenu(menuMgr, treeViewer); */
+		IActionBars bars = getViewSite().getActionBars();
+		IToolBarManager toolBar = bars.getToolBarManager();
+		toolBar.add( new RefreshAction() );
+	}
+
+	private void createTopMenu() {
+		IActionBars bars = getViewSite().getActionBars();
+		IMenuManager menu = bars.getMenuManager();
+		menu.removeAll();
+		for ( final IBundleRepository rep : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+			if ( treeViewer.getInput() == null ) {
+				treeViewer.setInput(rep);
+			}
+			
+			RepositoryAction action = new RepositoryAction(rep);
+			menu.add( action );			
+		}
+	}
+
+	private void createBody(Composite parent) {
+		// components
+		Composite control = new Composite(parent, SWT.NONE);
+		Tree tree = new Tree(control, SWT.NONE);
+		
+		// layout
+		control.setLayout( new GridLayout(1, false ) );
+		tree.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1) );
+		
+		// viewer
+		treeViewer = new TreeViewer(tree);
+		treeViewer.setContentProvider( new DefaultTreeContentProvider() {
+			public Object[] getChildren(Object parentElement) {
+				if ( parentElement instanceof ICompoundModelElement ) {
+					ICompoundModelElement model = (ICompoundModelElement) parentElement;
+					return model.children();
+				}
+				
+				return null;
+			}
+
+			public Object getParent(Object element) {
+				if ( element instanceof IModelElement ) {
+					IModelElement model = (IModelElement) element;
+					return model.getParent();
+				}
+				
+				return null;
+			}
+
+			public boolean hasChildren(Object element) {
+				if ( element instanceof ICompoundModelElement ) {
+					ICompoundModelElement model = (ICompoundModelElement) element;
+					return model.children().length > 0;
+				}
+				return false;
+			}
+
+			public Object[] getElements(Object inputElement) {
+				IBundleRepository rep = (IBundleRepository) inputElement;
+				return getBundles(rep);
+			}
+		});
+		
+		treeViewer.setComparator( new ModelElementComparator() );
+		
+		treeViewer.setLabelProvider( new ModelLabelProvider() );
+		
+		treeViewer.addDragSupport(DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DragSourceAdapter() {
+			@Override
+			public void dragFinished(DragSourceEvent event) {
+				// TODO Auto-generated method stub
+				super.dragFinished(event);
+			}
+
+			@Override
+			public void dragSetData(DragSourceEvent event) {
+				// TODO Auto-generated method stub
+				super.dragSetData(event);
+			}
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public void dragStart(DragSourceEvent event) {
+				if ( treeViewer.getSelection().isEmpty() ) {
+					IStructuredSelection sel = (IStructuredSelection) treeViewer.getSelection();
+					for ( Iterator<IModelElement> i = sel.iterator(); i.hasNext(); ) {
+						IModelElement e = i.next();
+						if ( e instanceof ISigilBundle ) {
+							event.data = e;
+						}
+						else {
+							event.doit = false;
+						}
+					}
+				}
+				else {
+					event.doit = false;
+				}
+			}
+		});
+	}
+
+	@Override
+	public void setFocus() {
+	}
+
+	public void repositoryChanged(RepositoryChangeEvent event) {
+		switch ( event.getType() ) {
+		case ADDED:
+			createTopMenu();
+			break;
+		case CHANGED:
+			if ( event.getRepository() == treeViewer.getInput() ) {
+				SigilUI.runInUI( new Runnable() {
+					public void run() {
+						treeViewer.refresh();
+					} 					
+				} );
+			}
+			break;
+		case REMOVED:
+			if ( event.getRepository() == treeViewer.getInput() ) {
+				treeViewer.setInput(null);
+			}
+			createTopMenu();
+		}
+	}
+	
+	private Object[] getBundles(IBundleRepository repository) {
+		final LinkedList<ISigilBundle> bundles = new LinkedList<ISigilBundle>();
+		repository.accept(new IRepositoryVisitor() {
+			public boolean visit(ISigilBundle bundle) {
+				bundles.add(bundle);
+				return true;
+			}
+		});
+		return bundles.toArray();
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
new file mode 100644
index 0000000..586375e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.core.widgets.GraphItem;
+import org.eclipse.zest.core.widgets.GraphNode;
+
+public class BundleConnectionHighlighter implements ISelectionChangedListener {
+
+	private BundleResolverView view;
+	
+	public BundleConnectionHighlighter(BundleResolverView view) {
+		this.view = view;
+	}
+	
+	public void selectionChanged(SelectionChangedEvent event) {
+		ISelection selection = event.getSelection();
+		if ( !selection.isEmpty() ) {
+			IStructuredSelection str = (IStructuredSelection) selection;
+
+			Object sel = str.getFirstElement();
+			
+			if ( sel instanceof ISigilBundle ) {
+				BundleGraph graph = (BundleGraph) view.getBundlegraph();
+
+				ISigilBundle selected = (ISigilBundle) sel;
+				Set<ISigilBundle> connected = graph.getTargets(selected);
+
+				highlightLinks(graph, selected, connected);
+				highlightBundles(graph, selected, connected);
+			}
+			else if ( sel instanceof Link ) {
+				GraphConnection c = (GraphConnection) findGraphItem(sel);
+				if ( c != null ) {
+					c.unhighlight();
+					c.setHighlightColor(ColorConstants.blue);
+					c.highlight();
+				}
+			}
+		}
+		else {
+			// TODO clear highlights...
+		}
+	}
+
+	private void highlightBundles(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+		for ( ISigilBundle bundle : graph.getBundles() ) {
+			GraphNode node = (GraphNode) findGraphItem(bundle);
+			if ( node != null ) {
+				node.unhighlight();
+
+				if ( bundle == selected ) {
+					node.setHighlightColor(ColorConstants.yellow);
+					node.highlight();
+				}
+				else if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+					if ( connected.contains(bundle) ) {
+						node.setHighlightColor(ColorConstants.lightBlue);
+						node.highlight();
+					}
+				}
+			}
+		}
+	}
+	
+	private void highlightLinks(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+		for ( Link l : graph.getLinks() ) {
+			GraphConnection c = (GraphConnection) findGraphItem(l);
+			if ( c != null ) {
+				c.unhighlight();
+
+				if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+					if ( l.getSource() == selected && connected.contains( l.getTarget() ) ) {
+						c.setHighlightColor(ColorConstants.lightBlue);
+						c.highlight();
+					}
+				}
+			}
+		}
+	}
+
+	private GraphItem findGraphItem(Object l) {
+		try {
+			return view.getGraphViewer().findGraphItem(l);
+		}
+		catch (ArrayIndexOutOfBoundsException e) {
+			// temporary fix for issue 
+			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=242523
+			return null;
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
new file mode 100644
index 0000000..8fad30f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public class BundleGraph {
+
+	private HashMap<ISigilBundle, LinkedList<Link>> lookup = new HashMap<ISigilBundle, LinkedList<Link>>();
+	private LinkedList<Link> links = new LinkedList<Link>();
+	private HashSet<ISigilBundle> bundles = new HashSet<ISigilBundle>();
+	
+	public void startResolution(IModelElement requirement) {
+	}
+
+	public void endResolution(IModelElement requirement, ISigilBundle target) {
+		ISigilBundle source = requirement.getAncestor(ISigilBundle.class);
+
+		bundles.add(source);
+		bundles.add(target);
+
+		LinkedList<Link> links = lookup.get(source);
+
+		if ( links == null ) {
+			links = new LinkedList<Link>();
+			lookup.put(source, links);
+		}
+
+		Link l = null;
+		for ( Link c : links ) {
+			if ( c.getTarget() == target ) {
+				l = c;
+				break;
+			}
+		}
+
+		if ( l == null ) {
+			l = new Link(source, target);
+			links.add(l);
+			this.links.add(l);
+		}
+
+		l.addRequirement(requirement);
+	}
+	
+	public List<Link> getLinks() {
+		return links;
+	}
+
+	public Set<ISigilBundle> getBundles() {
+		return bundles;
+	}
+
+	public Set<ISigilBundle> getTargets(ISigilBundle bundle) {
+		HashSet<ISigilBundle> targets = new HashSet<ISigilBundle>();
+		
+		for ( Link l : getLinks(bundle) ) {
+			targets.add(l.getTarget());
+		}
+		
+		return targets;
+	}
+
+	public List<Link> getLinks(ISigilBundle selected) {
+		List<Link> l = lookup.get(selected);
+		return l == null ? Collections.<Link>emptyList() : l;
+	}
+
+	public List<Link> getDependentLinks(ISigilBundle bundle) {
+		ArrayList<Link> found = new ArrayList<Link>(links.size());
+		
+		for  (Link l : links) {
+			if ( l.getTarget() == bundle ) {
+				found.add( l );
+			}
+		}
+		
+		found.trimToSize();
+		
+		return found;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
new file mode 100644
index 0000000..0c0547c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.zest.core.viewers.IGraphContentProvider;
+
+public class BundleGraphContentProvider implements IGraphContentProvider {
+
+	public Object[] getElements(Object input) {
+		BundleGraph graph = (BundleGraph) input;
+		return graph.getLinks().toArray();
+	}
+
+	public Object getDestination(Object element) {
+		Link l = (Link) element;
+		return l.isSatisfied() ? l.getTarget() : new Link.Unsatisfied();
+	}
+
+	public Object getSource(Object element) {
+		Link l = (Link) element;
+		return l.getSource();
+	}
+
+	public void dispose() {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+		
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
new file mode 100644
index 0000000..d48c253
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class BundleGraphLabelProvider extends LabelProvider {
+
+	private BundleResolverView view;
+	
+	public BundleGraphLabelProvider(BundleResolverView view) {
+		this.view = view;
+	}
+
+	@Override
+	public String getText(Object element) {
+		if ( element instanceof Link ) {
+			Link l = (Link) element;
+			if ( l.isSatisfied() ) {
+				if ( view.isDisplayed(BundleResolverView.LINK_LABELS) ) {
+					return view.getLinkText((Link) element);
+				}
+				else {
+					return "";
+				}
+			}
+			else {
+				return view.getLinkText((Link) element);				
+			}
+		}
+		else if ( element instanceof ISigilBundle ) {
+			ISigilBundle b = (ISigilBundle) element;
+			return b.getBundleInfo().getSymbolicName() + ": " + b.getBundleInfo().getVersion();
+		}
+		else if ( element instanceof Link.Unsatisfied ) {
+			return "unsatisfied";
+		}
+		else {
+			return "unknown:" + element;
+		}
+	}
+	
+	@Override
+	public Image getImage(Object element) {
+		Image result = null;
+		if ( element instanceof ISigilBundle ) {
+			result = SigilUI.cacheImage("icons/jar_obj.png", BundleGraphLabelProvider.class.getClassLoader());
+		}
+		else if ( element instanceof Link.Unsatisfied ) {
+			result = SigilUI.cacheImage("icons/error.gif", BundleGraphLabelProvider.class.getClassLoader());
+		}
+		
+		return result;
+	}	
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
new file mode 100644
index 0000000..4100ad8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class BundleGraphViewFilter extends ViewerFilter {
+
+	private BundleResolverView view;
+	
+	public BundleGraphViewFilter(BundleResolverView view) {
+		this.view = view;
+	}
+
+	@Override
+	public boolean select(Viewer viewer, Object parentElement, Object element) {
+		if ( !view.isDisplayed(BundleResolverView.LOCAL_LINKS) ) {
+			if ( element instanceof Link ) {
+				Link l = (Link) element;
+				return l.getSource() != l.getTarget();
+			}
+		}
+		if ( !view.isDisplayed(BundleResolverView.SATISFIED) ) {
+			if ( element instanceof Link ) {
+				Link l = (Link) element;
+				return !l.isSatisfied();
+			}
+			else if ( element instanceof ISigilBundle ) {
+				ISigilBundle bundle = (ISigilBundle) element;
+				for ( Link l : view.getBundlegraph().getLinks(bundle)) {
+					if ( !l.isSatisfied() ) {
+						return true;
+					}
+				}
+				return false;
+			}
+		}
+		
+		if ( !view.isDisplayed(BundleResolverView.UNSATISFIED) ) {
+			if ( element instanceof Link ) {
+				Link l = (Link) element;
+				return l.isSatisfied();
+			}
+			else if ( element instanceof Link.Unsatisfied ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
new file mode 100644
index 0000000..91c0dcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.zest.core.viewers.GraphViewer;
+import org.eclipse.zest.core.widgets.Graph;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.layouts.LayoutStyles;
+import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm;
+import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
+
+public class BundleResolverView extends ViewPart {
+
+	private static final String SHOW_LINK_LABELS = "Show link labels";
+	private static final String HIDE_LINK_LABELS = "Hide link labels";
+	private static final String SHOW_LOCAL_LINKS = "Show local links";
+	private static final String HIDE_LOCAL_LINKS = "Hide local links";
+	private static final String SHOW_DEPENDENTS = "Show dependents";
+	private static final String HIDE_DEPENDENTS = "Hide dependents";
+	private static final String SHOW_SATISFIED = "Show satisfied bundles";
+	private static final String HIDE_SATISFIED = "Hide satisfied bundles";
+	private static final String SHOW_UNSATISFIED = "Show unsatisfied bundles";
+	private static final String HIDE_UNSATISFIED = "Hide unsatisfied bundles";
+	private static final String SHOW_OPTIONAL = "Show optional dependencies";
+	private static final String HIDE_OPTIONAL = "Hide optional dependencies";
+	
+	public static final String LINK_LABELS = "link.labels";
+	public static final String LOCAL_LINKS = "local.links";
+	public static final String DEPENDENTS = "dependents";
+	public static final String SATISFIED = "satisified";
+	public static final String UNSATISFIED = "unsatisfied";
+	public static final String OPTIONAL = "optional";
+
+	private GraphViewer viewer;
+	private IModelElement current;
+	private Job job;
+	private int lastX;
+	private int lastY;	
+	
+	private Map<String, Boolean> displayed = new HashMap<String, Boolean>();
+		
+	private class ToggleDisplayAction extends Action {	
+		private String key;
+		private String showMsg;
+		private String hideMsg;
+
+		ToggleDisplayAction(String key, String showMsg, String hideMsg) {
+			this.key = key;
+			this.showMsg = showMsg;
+			this.hideMsg = hideMsg;
+			setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+		}
+		
+		@Override
+		public void run() {
+			BundleResolverView.this.setDisplayed( key, !BundleResolverView.this.isDisplayed(key) );
+			setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+		}
+	}
+	
+	public void setInput(final IModelElement element) {
+		if ( current == null || !current.equals(element) ) {
+			SigilCore.log( "Set input " + element );
+			current = element;
+			redraw();
+		}
+	}
+	
+	public void setDisplayed(String key, boolean b) {
+		displayed.put(key, b);
+		
+		if ( key == DEPENDENTS ) {
+			int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+			viewer.setLayoutAlgorithm( b ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+			redraw();
+		}
+		else if ( key == OPTIONAL ) {
+			redraw();
+		}
+		else if ( key == SATISFIED || key == UNSATISFIED ) {
+			viewer.refresh();
+		}
+	}
+
+	public boolean isDisplayed(String key) {
+		return displayed.get(key);
+	}
+
+	@Override
+	public void setFocus() {
+	}
+	
+	@Override
+    public void createPartControl( Composite parent ) {
+		init();
+		createViewer(parent);
+		createListeners();
+		createMenu();
+    }
+	
+	private void init() {
+		displayed.put(LINK_LABELS, false);
+		displayed.put(LOCAL_LINKS, false);
+		displayed.put(DEPENDENTS, false);
+		displayed.put(OPTIONAL, false);
+		displayed.put(SATISFIED, true);
+		displayed.put(UNSATISFIED, true);
+	}
+
+	public BundleGraph getBundlegraph() {
+		return (BundleGraph) viewer.getInput();
+	}
+
+	GraphViewer getGraphViewer() {
+		return viewer;
+	}
+
+	String getLinkText(Link link) {
+		StringBuffer buf = new StringBuffer();
+
+		for ( IModelElement e : link.getRequirements() ) {
+			if ( buf.length() > 0 ) {
+				buf.append( "\n" );
+			}
+			if ( e instanceof IPackageImport ) {
+				IPackageImport pi = (IPackageImport) e;
+				buf.append( "import " + pi.getPackageName() + " : " + pi.getVersions() + ": " + (pi.isOptional() ? "optional" : "mandatory" ) );
+			}
+			else if ( e instanceof IRequiredBundle ) {
+				IRequiredBundle rb = (IRequiredBundle) e;
+				buf.append( "required bundle " + rb.getSymbolicName() + " : " + rb.getVersions() + ": " + (rb.isOptional() ? "optional" : "mandatory" ) );
+			}
+		}
+		
+		return buf.toString();
+	}	
+
+	private synchronized void redraw() {
+		final IModelElement element = current;
+		if ( job != null ) {
+			job.cancel();
+		}
+		
+		job = new Job("Resolving " + current) {
+			@Override
+			protected IStatus run(IProgressMonitor progress) {
+				try {
+					resolve(element, progress);
+					return Status.OK_STATUS;
+				} catch (CoreException e) {
+					return e.getStatus();
+				}
+			}
+		};
+		job.schedule();
+	}
+
+	private void resolve(IModelElement element, IProgressMonitor progress) throws CoreException {
+		final BundleGraph graph = new BundleGraph();
+		
+		IResolutionMonitor monitor = new ResolutionMonitorAdapter(progress) {
+			@Override
+			public void startResolution(IModelElement requirement) {
+				graph.startResolution(requirement);
+			}
+
+			@Override
+			public void endResolution(IModelElement requirement, ISigilBundle provider) {
+				graph.endResolution(requirement, provider);
+			}
+		};
+
+		ISigilProjectModel project = findProject(element);
+		IRepositoryManager repository = SigilCore.getRepositoryManager(project);
+		
+		int options = ResolutionConfig.IGNORE_ERRORS;
+		
+		if ( isDisplayed(DEPENDENTS) ) {
+			options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+		}
+		if ( isDisplayed(OPTIONAL) ) {
+			options |= ResolutionConfig.INCLUDE_OPTIONAL;
+		}
+		
+		ResolutionConfig config = new ResolutionConfig(options);
+		
+		try {
+			repository.getBundleResolver().resolve(element, config, monitor);
+		} catch (ResolutionException e) {
+			throw SigilCore.newCoreException("Failed to resolve " + element, e);
+		}
+		
+		SigilUI.runInUI( new Runnable() {
+			public void run() {
+				viewer.setInput(graph);
+				addCustomUIElements();
+			}			
+		} );
+	}
+
+	private static ISigilProjectModel findProject(IModelElement element) {
+		if ( element == null ) {
+			return null;
+		}
+		if ( element instanceof ISigilProjectModel ) {
+			return (ISigilProjectModel) element;
+		}
+		
+		return element.getAncestor(ISigilProjectModel.class);
+	}
+
+	@SuppressWarnings("unchecked")
+	private void addCustomUIElements() {
+		if ( !isDisplayed(LINK_LABELS) ) {
+			for ( GraphConnection c : (List<GraphConnection>) viewer.getGraphControl().getConnections() ) {
+				if ( c.getData() instanceof Link ) {
+					c.setTooltip(buildToolTip((Link) c.getData()));
+				}
+			}
+		}
+	}
+
+	private IFigure buildToolTip(Link link) {
+		Label l = new Label();
+		l.setText(getLinkText(link));
+		return l;
+	}
+
+	private void createViewer(Composite parent) {
+		parent.setLayout( new FillLayout() );
+    	viewer = new GraphViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+    	IContentProvider contentProvider = new BundleGraphContentProvider();
+		viewer.setContentProvider(contentProvider);
+		viewer.setLabelProvider(new BundleGraphLabelProvider(this));
+		viewer.addFilter( new BundleGraphViewFilter(this) );
+		
+		
+		int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+		viewer.setLayoutAlgorithm( isDisplayed(DEPENDENTS) ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+		viewer.addSelectionChangedListener( new BundleConnectionHighlighter(this) );
+		viewer.setInput(new BundleGraph());
+	}
+
+	private void createMenu() {
+		IActionBars action = getViewSite().getActionBars(); 
+		action.getMenuManager().add(new ToggleDisplayAction( LINK_LABELS, SHOW_LINK_LABELS, HIDE_LINK_LABELS ));
+		action.getMenuManager().add(new ToggleDisplayAction( LOCAL_LINKS, SHOW_LOCAL_LINKS, HIDE_LOCAL_LINKS ));
+		action.getMenuManager().add(new ToggleDisplayAction( DEPENDENTS, SHOW_DEPENDENTS, HIDE_DEPENDENTS ));
+		action.getMenuManager().add(new ToggleDisplayAction( OPTIONAL, SHOW_OPTIONAL, HIDE_OPTIONAL ));
+		action.getMenuManager().add(new ToggleDisplayAction( SATISFIED, SHOW_SATISFIED, HIDE_SATISFIED ));
+		action.getMenuManager().add(new ToggleDisplayAction( UNSATISFIED, SHOW_UNSATISFIED, HIDE_UNSATISFIED ));
+		action.updateActionBars();
+	}
+
+	private void createListeners() {
+		IPartService ps = (IPartService) getViewSite().getService(IPartService.class);
+		ps.addPartListener( new EditorViewPartListener(this) );
+		viewer.getGraphControl().addControlListener( new ControlAdapter() {
+			@Override
+			public void controlResized(ControlEvent e) {
+				Graph g = (Graph) e.getSource();
+				int x = g.getSize().x;
+				int y = g.getSize().y;
+				if ( lastX != x || lastY != y ) {
+					lastX = x;
+					lastY = y;
+					redraw();
+				}
+			}
+		} );
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
new file mode 100644
index 0000000..78bceb7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPartReference;
+
+public class EditorViewPartListener implements IPartListener2 {
+
+	private BundleResolverView bundleResolverView;
+	
+	public EditorViewPartListener(BundleResolverView bundleResolverView) {
+		this.bundleResolverView = bundleResolverView;
+	}
+
+	public void partActivated(IWorkbenchPartReference partRef) {
+		checkRef(partRef);
+	}
+
+	public void partBroughtToTop(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partClosed(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partDeactivated(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partHidden(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partInputChanged(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partOpened(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	public void partVisible(IWorkbenchPartReference partRef) {
+		// no action
+	}
+
+	private void checkRef(IWorkbenchPartReference partRef) {
+		IEditorPart editor = partRef.getPage().getActiveEditor();
+		if ( editor != null ) {
+			IEditorInput input = editor.getEditorInput();
+			if ( input instanceof IFileEditorInput ) {
+				IFileEditorInput f = (IFileEditorInput) input;
+				IProject project = f.getFile().getProject();
+				try {
+					ISigilProjectModel model = SigilCore.create(project);
+					if ( model != null ) {
+						bundleResolverView.setInput(model);
+					}
+				} catch (CoreException e) {
+					// TODO Auto-generated catch block
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
new file mode 100644
index 0000000..35591a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class Link {	
+	public static class Unsatisfied {
+
+	}
+
+	private ISigilBundle source;
+	private ISigilBundle target;
+	
+	private LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();
+	private static final Comparator<IModelElement> comparator = new Comparator<IModelElement>() {
+
+		public int compare(IModelElement o1, IModelElement o2) {
+			if (o1 instanceof IRequiredBundle) {
+				if ( o2 instanceof IRequiredBundle) {
+					return compareBundles( (IRequiredBundle) o1, (IRequiredBundle) o2 );
+				}
+				else {
+					return -1;
+				}
+			}
+			else {
+				if ( o2 instanceof IRequiredBundle ) {
+					return 1;
+				}
+				else {
+					return compareNonBundles( o1, o2 );
+				}
+			}
+		}
+
+		private int compareNonBundles(IModelElement o1, IModelElement o2) {
+			if (o1 instanceof IPackageImport) {
+				if ( o2 instanceof IPackageImport) {
+					return compareImports( (IPackageImport) o1, (IPackageImport) o2 );
+				}
+				else {
+					return -1;
+				}
+			}
+			else {
+				if ( o2 instanceof IPackageImport ) {
+					return 1;
+				}
+				else {
+					return 0;
+				}
+			}
+		}
+
+		private int compareImports(IPackageImport o1, IPackageImport o2) {
+			return o1.getPackageName().compareTo( o2.getPackageName() );
+		}
+
+		private int compareBundles(IRequiredBundle o1, IRequiredBundle o2) {
+			return o1.getSymbolicName().compareTo( o2.getSymbolicName() );
+		}
+		
+	};
+	
+	public Link(ISigilBundle source, ISigilBundle target) {
+		this.source = source;
+		this.target = target;
+	}
+
+	public ISigilBundle getSource() {
+		return source;
+	}
+	
+	public ISigilBundle getTarget() {
+		return target;
+	}
+	
+	public boolean isSatisfied() {
+		return target != null;
+	}
+
+	public void addRequirement(IModelElement requirement) {
+		requirements.add(requirement);
+		Collections.sort(requirements, comparator);
+	}
+	
+	public String toString() {
+		return "Link[" + source + "->" + target + "]";
+	}
+
+	public List<IModelElement> getRequirements() {
+		return requirements;
+	}
+
+	public boolean isOptional() {
+		for ( IModelElement e : requirements ) {
+			if ( e instanceof IPackageImport ) {
+				IPackageImport pi = (IPackageImport) e;
+				if ( !pi.isOptional() ) {
+					return false;
+				}
+			}
+			else if ( e instanceof IRequiredBundle ) {
+				IRequiredBundle rb = (IRequiredBundle) e;
+				if ( !rb.isOptional() ) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
new file mode 100644
index 0000000..bc4a39d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public abstract class SigilNewResourceWizard extends BasicNewResourceWizard implements INewWizard {
+
+    protected void selectRevealAndShow(IFile file) {
+        selectAndReveal(file);
+    
+        // Open editor on new file.
+        IWorkbenchWindow dw = getWorkbench().getActiveWorkbenchWindow();
+        try {
+            if (dw != null) {
+                IWorkbenchPage page = dw.getActivePage();
+                if (page != null) {
+                    IDE.openEditor(page, file, true);
+                }
+            }
+        }
+        catch (PartInitException e) {
+            MessageDialog.openError(Display.getCurrent().getActiveShell(), "Initialisation error",
+                    "Failed to open " + file);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
new file mode 100644
index 0000000..cbab33b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class WorkspaceContentProvider implements ITreeContentProvider {
+	
+	private final boolean includeNonSigil;
+	private final boolean includeClosed;
+
+	public WorkspaceContentProvider(boolean includeNonSigil, boolean includeClosed) {
+		this.includeNonSigil = includeNonSigil;
+		this.includeClosed = includeClosed;
+	}
+
+	public Object[] getChildren(Object parentElement) {
+		Object[] result = null;
+		
+		if(parentElement instanceof IWorkspace) {
+			IProject[] projects = ((IWorkspace) parentElement).getRoot().getProjects();
+			if(includeNonSigil && includeClosed) {
+				result = projects;
+			} else {
+				List<IProject> includedProjects = new ArrayList<IProject>(projects.length);
+				for (IProject project : projects) {
+					if(!includeClosed && !project.isOpen()) {
+						continue;
+					}
+					
+					if(!includeNonSigil) {
+						try {
+							if(project.getNature(SigilCore.NATURE_ID) == null) {
+								continue;
+							}
+						} catch (CoreException e) {
+							continue;
+						}
+					}
+					
+					includedProjects.add(project);
+				}
+				result = includedProjects.toArray(new IProject[includedProjects.size()]);
+			}
+		} else if(parentElement instanceof IContainer) {
+			try {
+				IResource[] members = ((IContainer) parentElement).members();
+				List<IResource> children = new ArrayList<IResource>(members.length);
+				for (int i = 0; i < members.length; i++) {
+				    if (members[i].getType() != IResource.FILE) {
+				        children.add(members[i]);
+				    }
+				}
+				result = children.toArray(new IResource[children.size()]);
+			} catch (CoreException e) {
+				// Shouldn't happen
+			}
+		}
+		
+		return result;
+	}
+
+	public Object getParent(Object element) {
+		if(element instanceof IResource) {
+			return ((IResource) element).getParent();
+		}
+		return null;
+	}
+
+	public boolean hasChildren(Object element) {
+		return (element instanceof IContainer) && ((IContainer) element).isAccessible();
+	}
+
+	public Object[] getElements(Object inputElement) {
+		return getChildren(inputElement);
+	}
+
+	public void dispose() {
+	}
+
+	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
new file mode 100644
index 0000000..8f926bf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.wizard.SigilNewResourceWizard;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizard extends SigilNewResourceWizard implements IExecutableExtension {
+
+    private SigilProjectWizardFirstPage firstPage;
+    private SigilProjectWizardSecondPage secondPage;
+    
+    private String name;
+    
+    public static final IPath SIGIL_PROJECT_PATH = new Path( SigilCore.SIGIL_PROJECT_FILE );
+	private IConfigurationElement config;
+    
+    public void init(IWorkbench workbench, IStructuredSelection currentSelection) {
+        super.init(workbench, currentSelection);
+
+        firstPage = new SigilProjectWizardFirstPage();
+        firstPage.setInitialProjectName(name);
+        secondPage = new SigilProjectWizardSecondPage(firstPage);
+        
+        addPage(firstPage);
+        addPage(secondPage);
+    }
+    
+    private void finishPage(IProgressMonitor monitor) throws CoreException, InterruptedException {
+        secondPage.performFinish(monitor);
+        
+        IProject newProject = firstPage.getProjectHandle();
+        
+        if ( newProject != null && newProject.exists() ) {
+            IFile file = newProject.getFile( SigilProjectWizard.SIGIL_PROJECT_PATH );
+            
+            selectRevealAndShow( file ); 
+           
+            new Job("Check OSGi Install" ) {
+				@Override
+				protected IStatus run(IProgressMonitor monitor) {
+                    // prompt for osgi home if not already set.
+            		SigilCore.getInstallManager().getDefaultInstall();
+            		return Status.OK_STATUS;
+				}
+            }.schedule();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jface.wizard.Wizard#performFinish()
+     */
+    @Override
+    public boolean performFinish() {
+        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+        
+        IWorkspaceRunnable op= new IWorkspaceRunnable() {
+            public void run(IProgressMonitor monitor) throws CoreException {
+                try {
+                    finishPage(monitor);
+                } catch (InterruptedException e) {
+                    throw new OperationCanceledException(e.getMessage());
+                }
+            }
+        };
+        
+        try {
+            workspace.run(op, Job.getJobManager().createProgressGroup());
+        }
+        catch (CoreException e) {
+            SigilCore.error( "Failed to complete project wizard", e);
+            return false;
+        }
+        
+        BasicNewProjectResourceWizard.updatePerspective(config);
+        return true;
+    }
+
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public String getName() {
+		return name;
+	}
+    
+    @Override
+    public boolean performCancel() {
+    	secondPage.performCancel();
+    	return super.performCancel();
+    }
+
+	public void setInitializationData(IConfigurationElement config, String propertyName, Object data)
+			throws CoreException {
+		this.config = config;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
new file mode 100644
index 0000000..78eaf19
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardFirstPage extends WizardNewProjectCreationPage {
+
+	private volatile String description = "";
+	private volatile Version version = new Version(1, 0, 0);
+	private volatile String vendor = "";
+	private volatile String name = "";
+	
+	private Text txtDescription;
+    private Text txtVersion;
+	private Text txtVendor;
+	private Text txtName;
+
+	public SigilProjectWizardFirstPage() {
+        super("newSigilProjectPage");
+        setTitle( "Sigil Project" );
+        setDescription( "Create a new Sigil project" );
+    }
+
+    public boolean isInWorkspace() {
+        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+        
+        IPath defaultDefaultLocation = workspace.getRoot().getLocation();
+        
+        return defaultDefaultLocation.isPrefixOf( getLocationPath() );
+    }
+    
+    @Override
+    public boolean isPageComplete() {
+    	boolean result = super.isPageComplete();
+    	return result;
+    }
+    
+    @Override
+    public void createControl(Composite parent) {
+    	FieldDecoration infoDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
+    	
+    	// Create controls
+    	super.createControl(parent);
+    	Composite control = (Composite) getControl();
+    	
+    	Group grpProjectSettings = new Group(control, SWT.NONE);
+    	grpProjectSettings.setText("Project Settings");
+    	
+    	new Label(grpProjectSettings, SWT.NONE).setText("Version:");
+    	txtVersion = new Text(grpProjectSettings, SWT.BORDER);
+    	
+    	new Label(grpProjectSettings, SWT.NONE).setText("Name:");
+    	txtName = new Text(grpProjectSettings, SWT.BORDER);
+    	
+    	ControlDecoration txtNameDecor = new ControlDecoration(txtName, SWT.LEFT | SWT.CENTER);
+    	txtNameDecor.setImage(infoDecor.getImage());
+    	txtNameDecor.setDescriptionText("Defines a human-readable name for the bundle");
+    	
+    	new Label(grpProjectSettings, SWT.NONE).setText("Description:");
+    	txtDescription = new Text(grpProjectSettings, SWT.BORDER);
+    	
+    	ControlDecoration txtDescDecor = new ControlDecoration(txtDescription, SWT.LEFT | SWT.CENTER);
+    	txtDescDecor.setImage(infoDecor.getImage());
+    	txtDescDecor.setDescriptionText("Defines a short human-readable description for the bundle");
+    	
+    	new Label(grpProjectSettings, SWT.NONE).setText("Provider:");
+    	txtVendor = new Text(grpProjectSettings, SWT.BORDER);
+    	
+    	ControlDecoration txtVendorDecor = new ControlDecoration(txtVendor, SWT.LEFT | SWT.CENTER);
+    	txtVendorDecor.setImage(infoDecor.getImage());
+    	txtVendorDecor.setDescriptionText("The name of the company, organisation or individual providing the bundle");
+    	    	
+    	// Set values
+    	txtDescription.setText(description);
+    	txtVersion.setText(version.toString());
+    	txtVendor.setText(vendor);
+    	txtName.setText(name);
+    	
+    	// Add listeners
+    	ModifyListener txtModListener = new ModifyListener() {
+			public void modifyText(ModifyEvent e) {
+				description = txtDescription.getText();
+				vendor = txtVendor.getText();
+				name = txtName.getText();
+				
+				validateSettings();
+			}
+    	};
+    	txtDescription.addModifyListener(txtModListener);
+    	txtVersion.addModifyListener(txtModListener);
+    	txtVendor.addModifyListener(txtModListener);
+    	txtName.addModifyListener(txtModListener);
+    	
+    	// Layout
+    	control.setLayout(new GridLayout());
+    	grpProjectSettings.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+    	grpProjectSettings.setLayout(new GridLayout(2, false));
+    	txtDescription.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+    	txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+    	txtVendor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+    	txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+    }
+
+    private void validateSettings() {
+    	try {
+			version = new Version(txtVersion.getText());
+		} catch (IllegalArgumentException e) {
+			version = null;
+			setErrorMessage("Invalid version");
+			setPageComplete(false);
+			return;
+		}
+		
+		setErrorMessage(null);
+		setPageComplete(true);
+	}
+
+	public Version getVersion() {
+		return version;
+	}
+
+	public String getVendor() {
+		return vendor;
+	}
+	
+	public String getDescription() {
+		return description;
+	}
+	
+	public String getName() {
+		return name;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
new file mode 100644
index 0000000..3b2592d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.PreferenceConstants;
+import org.eclipse.jdt.ui.wizards.JavaCapabilityConfigurationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardSecondPage extends JavaCapabilityConfigurationPage {
+    
+    private SigilProjectWizardFirstPage firstPage;
+    private IProject currentProject;
+    private URI currentProjectLocation;
+    private boolean created;
+    
+    public SigilProjectWizardSecondPage(SigilProjectWizardFirstPage firstPage) {
+        this.firstPage = firstPage;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+    	super.setVisible(visible);
+        if (visible) {
+            changeToNewProject();
+        } else {
+            removeProject();
+        }
+    }
+    
+    @Override
+    protected boolean useNewSourcePage() {
+        return true;
+    }
+    
+    protected void performFinish(IProgressMonitor monitor) throws CoreException, InterruptedException {
+    	changeToNewProject();
+        updateProject(monitor);
+    }
+    
+    private void changeToNewProject() {
+    	if ( !created ) {
+            IWorkspace workspace = ResourcesPlugin.getWorkspace();
+            
+            IWorkspaceRunnable op= new IWorkspaceRunnable() {
+                public void run(IProgressMonitor monitor) throws CoreException {
+                    try {
+                        updateProject(monitor);
+                    } catch (InterruptedException e) {
+                        throw new OperationCanceledException(e.getMessage());
+                    }
+                }
+            };
+            
+            try {
+                workspace.run(op, Job.getJobManager().createProgressGroup());
+                setErrorMessage(null);
+                setPageComplete(true);
+                created = true;
+            }
+            catch (CoreException e) {
+            	SigilCore.error("Failed to run workspace job", e);
+            }        
+    	}
+    }
+    
+    private void removeProject() {
+        if (currentProject == null || !currentProject.exists()) {
+            return;
+        }
+        
+        IWorkspaceRunnable op= new IWorkspaceRunnable() {
+            public void run(IProgressMonitor monitor) throws CoreException {
+                doRemoveProject(monitor);
+            }
+        };
+    
+        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+        
+        try {
+            workspace.run(op, Job.getJobManager().createProgressGroup());
+        }
+        catch (CoreException e) {
+        	SigilCore.error("Failed to run workspace job", e);
+        }
+        finally {
+        	created = false;
+        }
+    }
+    
+    private void updateProject(IProgressMonitor monitor) throws CoreException, InterruptedException {
+        currentProject = firstPage.getProjectHandle();
+        currentProjectLocation= getProjectLocationURI();
+        
+        String description = firstPage.getDescription();
+        Version projectVersion = firstPage.getVersion();
+        String vendor = firstPage.getVendor();
+        String name = firstPage.getName();
+        
+        createProject( currentProject, currentProjectLocation, monitor);
+
+        IPath src = createSourcePath();
+        
+        IPath output = getOutputLocation();
+        
+        if ( output.segmentCount() == 0 ) {
+        	output = new Path( currentProject.getName() ).append( "build" ).append( "classes" );
+        }
+        
+        IClasspathEntry[] entries = getProjectClassPath(src);
+        
+    	SigilCore.makeSigilProject(currentProject, monitor);
+    	
+        init(JavaCore.create(currentProject), output.makeRelative(), entries, false);
+
+        configureJavaProject(new SubProgressMonitor(monitor, 3));
+        
+        configureSigilProject( currentProject, description, projectVersion, vendor, name, src, monitor );
+    }
+    
+	private IPath createSourcePath() throws CoreException {
+        IPath projectPath = currentProject.getFullPath();
+        IPath src = new Path( "src" );
+        IFolder f = currentProject.getFolder( src );
+        if ( !f.getLocation().toFile().exists() ) {
+        	f.create(true, true, null);
+        }
+        
+        return projectPath.append(src);
+	}
+
+	final void doRemoveProject(IProgressMonitor monitor) throws CoreException {
+        final boolean noProgressMonitor= (currentProjectLocation == null); // inside workspace
+        
+        if (monitor == null || noProgressMonitor) {
+            monitor= new NullProgressMonitor();
+        }
+        monitor.beginTask("Remove project", 3); 
+        try {
+            try {
+                boolean removeContent= currentProject.isSynchronized(IResource.DEPTH_INFINITE);
+                currentProject.delete(removeContent, false, new SubProgressMonitor(monitor, 2));
+                
+            } finally {
+            }
+        } finally {
+            monitor.done();
+            currentProject= null;
+        }        
+    }
+        
+    private IClasspathEntry[] getProjectClassPath(IPath src) throws CoreException {
+        List<IClasspathEntry> cpEntries= new ArrayList<IClasspathEntry>();
+        cpEntries.add(JavaCore.newSourceEntry(src));
+        cpEntries.addAll(Arrays.asList(getDefaultClasspathEntry()));
+        cpEntries.add(JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH)));
+        IClasspathEntry[] entries= cpEntries.toArray(new IClasspathEntry[cpEntries.size()]);
+        
+        return entries;
+    }
+    
+    private IClasspathEntry[] getDefaultClasspathEntry() {
+        IClasspathEntry[] defaultJRELibrary= PreferenceConstants.getDefaultJRELibrary();
+        /*String compliance= firstPage.getCompilerCompliance();
+        IPath jreContainerPath= new Path(JavaRuntime.JRE_CONTAINER);
+        if (compliance == null || defaultJRELibrary.length > 1 || !jreContainerPath.isPrefixOf(defaultJRELibrary[0].getPath())) {
+            // use default
+            return defaultJRELibrary;
+        }
+        IVMInstall inst= firstPage.getJVM();
+        if (inst != null) {
+            IPath newPath= jreContainerPath.append(inst.getVMInstallType().getId()).append(inst.getName());
+            return new IClasspathEntry[] { JavaCore.newContainerEntry(newPath) };
+        }*/
+        return defaultJRELibrary;
+    }
+    
+    
+    private void configureSigilProject( IProject project, String description, Version projectVersion, String vendorName, String bundleName, IPath src, IProgressMonitor monitor ) throws CoreException {
+        ISigilProjectModel sigil = SigilCore.create(project);
+        IClasspathEntry cp = JavaCore.newSourceEntry(src);
+        String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry(cp );
+        
+        ISigilBundle bundle = sigil.getBundle();
+        bundle.addClasspathEntry(encodedClasspath);
+        
+        if(description != null) {
+        	bundle.getBundleInfo().setDescription(description);
+        }
+		if(projectVersion != null) {
+        	bundle.setVersion(projectVersion);
+        }
+		if(vendorName != null) {
+			bundle.getBundleInfo().setVendor(vendorName);
+		}
+		if(bundleName != null) {
+			bundle.getBundleInfo().setName(bundleName);
+		}
+        sigil.save(monitor);
+    }
+    
+    
+    private URI getProjectLocationURI() throws CoreException {
+        if (firstPage.isInWorkspace()) {
+            return null;
+        }
+        return firstPage.getLocationURI();
+    }
+    
+    @Override
+    public boolean isPageComplete() {
+    	boolean result = super.isPageComplete();
+    	return result;
+    }
+    
+    protected void performCancel() {
+    	if(currentProject != null) {
+    		removeProject();
+    	}
+    }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
new file mode 100644
index 0000000..c2403e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+
+public class RepositoryWizard extends Wizard {
+	
+	private IRepositoryModel model;
+	
+	@Override
+	public boolean performFinish() {
+		for ( IWizardPage page : getPages() ) {
+			if ( page instanceof RepositoryWizardPage ) {
+				RepositoryWizardPage rwp = (RepositoryWizardPage) page;
+				rwp.storeFields();
+			}
+		}
+		return true;
+	}
+	
+	public IRepositoryModel getModel() {
+		return model;
+	}
+
+	public void init(IRepositoryModel model) {
+		this.model = model;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
new file mode 100644
index 0000000..9a22aa4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class RepositoryWizardPage extends WizardPage {
+
+	private StringFieldEditor nameEditor;
+	private ArrayList<FieldEditor> editors = new ArrayList<FieldEditor>();
+	private RepositoryWizard wizard;
+	
+	protected RepositoryWizardPage(String pageName, RepositoryWizard parent) {
+		super(pageName);
+		setTitle(pageName);
+		this.wizard = parent;
+	}
+	
+	public abstract void createFieldEditors();
+
+	public void addField(FieldEditor editor) {
+		editors.add( editor );
+	}
+	
+	public void createControl(Composite parent) {
+		Composite control = new Composite(parent, SWT.NONE);
+		setControl(control);
+		
+		if ( getModel().getType().isDynamic() ) {
+			nameEditor = new StringFieldEditor("name", "Name:", control);
+			nameEditor.setStringValue(getModel().getName());
+			nameEditor.getTextControl(getFieldEditorParent()).addModifyListener( new ModifyListener() {
+				public void modifyText(ModifyEvent e) {
+					checkPageComplete();
+				}
+			});
+		}
+		
+		createFieldEditors();
+
+		int cols = nameEditor == null ? 0 : nameEditor.getNumberOfControls();
+		for ( FieldEditor e : editors ) {
+			cols = Math.max(cols, e.getNumberOfControls());
+		}
+		
+		control.setLayout( new GridLayout(cols, false) );
+		
+		if ( nameEditor != null ) {
+			nameEditor.fillIntoGrid(getFieldEditorParent(), cols);
+		}
+
+		for ( FieldEditor e : editors ) {
+			e.fillIntoGrid(getFieldEditorParent(), cols);
+			e.setPreferenceStore(getModel().getPreferences());
+			e.load();
+		}
+		
+		checkPageComplete();
+	}
+
+	protected void checkPageComplete() {
+		if ( nameEditor != null ) {
+			setPageComplete(nameEditor.getStringValue().length() > 0);
+		}
+	}
+
+	public IRepositoryModel getModel() {
+		return wizard.getModel();
+	}
+
+	protected Composite getFieldEditorParent() {
+		return (Composite) getControl();
+	}
+
+	public void storeFields() {
+		getModel().setName(nameEditor.getStringValue());
+		for ( FieldEditor e : editors ) {
+			e.store();
+		}
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.updatesite/.project b/sigil/org.cauldron.sigil.updatesite/.project
new file mode 100644
index 0000000..b538dcd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.updatesite/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.updatesite</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.pde.UpdateSiteBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.UpdateSiteNature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.updatesite/site.xml b/sigil/org.cauldron.sigil.updatesite/site.xml
new file mode 100644
index 0000000..6b32f0a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.updatesite/site.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<site>
+   <feature url="features/org.cauldron.sigil.core_0.3.0.jar" id="org.cauldron.sigil.core" version="0.3.0">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.1.jar" id="org.cauldron.sigil.core" version="0.3.1">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.2.jar" id="org.cauldron.sigil.core" version="0.3.2">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.3.jar" id="org.cauldron.sigil.core" version="0.3.3">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.4.jar" id="org.cauldron.sigil.core" version="0.3.4">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.5.jar" id="org.cauldron.sigil.core" version="0.3.5">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.6.jar" id="org.cauldron.sigil.core" version="0.3.6">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.7.jar" id="org.cauldron.sigil.core" version="0.3.7">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.8.jar" id="org.cauldron.sigil.core" version="0.3.8">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.9.jar" id="org.cauldron.sigil.core" version="0.3.9">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.10.jar" id="org.cauldron.sigil.core" version="0.3.10">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.11.jar" id="org.cauldron.sigil.core" version="0.3.11">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.12.jar" id="org.cauldron.sigil.core" version="0.3.12">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.13.jar" id="org.cauldron.sigil.core" version="0.3.13">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.14.jar" id="org.cauldron.sigil.core" version="0.3.14">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.15.jar" id="org.cauldron.sigil.core" version="0.3.15">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.16.jar" id="org.cauldron.sigil.core" version="0.3.16">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.17.jar" id="org.cauldron.sigil.core" version="0.3.17">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.3.18.jar" id="org.cauldron.sigil.core" version="0.3.18">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.0.jar" id="org.cauldron.sigil.core" version="0.4.0">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.1.jar" id="org.cauldron.sigil.core" version="0.4.1">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.2.jar" id="org.cauldron.sigil.core" version="0.4.2">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.3.jar" id="org.cauldron.sigil.core" version="0.4.3">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.4.jar" id="org.cauldron.sigil.core" version="0.4.4">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.5.jar" id="org.cauldron.sigil.core" version="0.4.5">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.6.jar" id="org.cauldron.sigil.core" version="0.4.6">
+      <category name="Sigil"/>
+   </feature>
+   <feature url="features/org.cauldron.sigil.core_0.4.7.jar" id="org.cauldron.sigil.core" version="0.4.7">
+      <category name="Sigil"/>
+   </feature>
+   <category-def name="Sigil" label="Sigil">
+      <description>
+         Sigil is an SDK for developing applications to be deployed on the Newton framework (http://newton.codecauldron.org)
+      </description>
+   </category-def>
+</site>
diff --git a/sigil/org.cauldron.sigil.utils/.classpath b/sigil/org.cauldron.sigil.utils/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<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.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.utils/.project b/sigil/org.cauldron.sigil.utils/.project
new file mode 100644
index 0000000..0e1390a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.cauldron.sigil.utils</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..0710b5a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Tue Aug 12 11:45:38 BST 2008
+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.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7df73f4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Utils Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.utils;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Require-Bundle: org.eclipse.core.runtime
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.eclipse.core.expressions,
+ org.eclipse.core.resources,
+ org.eclipse.ui
+Export-Package: org.cauldron.sigil.utils
diff --git a/sigil/org.cauldron.sigil.utils/build.properties b/sigil/org.cauldron.sigil.utils/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml
diff --git a/sigil/org.cauldron.sigil.utils/plugin.xml b/sigil/org.cauldron.sigil.utils/plugin.xml
new file mode 100644
index 0000000..0d14f33
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/plugin.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+   <extension
+         point="org.eclipse.core.expressions.propertyTesters">
+      <propertyTester
+            class="org.cauldron.sigil.utils.properties.EditorPropertyTester"
+            id="org.cauldron.sigil.utils.editorPropertyTester"
+            namespace="org.cauldron.sigil"
+            properties="isEditorOfType"
+            type="org.eclipse.ui.IWorkbenchPart">
+      </propertyTester>
+      <propertyTester
+            class="org.cauldron.sigil.utils.properties.PartKindPropertyTester"
+            id="org.cauldron.sigil.runtime.PartKindPropertyTester"
+            namespace="org.cauldron.sigil"
+            properties="partKind,partId"
+            type="org.eclipse.ui.IWorkbenchPart">
+      </propertyTester>
+      <propertyTester
+            class="org.cauldron.sigil.utils.properties.ResourceTypePropertyTester"
+            id="org.cauldron.sigil.utils.ResourcePropertyTester"
+            namespace="org.cauldron.sigil"
+            properties="isResourceOfType"
+            type="org.eclipse.core.resources.IResource">
+      </propertyTester>
+   </extension>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java
new file mode 100644
index 0000000..401c390
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import java.util.regex.Pattern;
+
+public class GlobCompiler {
+	public static final Pattern compile(String glob) {
+		char[] chars = glob.toCharArray();
+		if ( chars.length > 0 ) {
+			StringBuilder builder = new StringBuilder(chars.length + 5);
+	
+			builder.append('^');
+			
+			for (char c : chars) {
+				switch ( c ) {
+				case '*':
+					builder.append(".*");
+					break;
+				case '.':
+					builder.append("\\.");
+					break;
+				case '$':
+					builder.append( "\\$" );
+					break;
+				default:
+					builder.append( c );
+				}
+			}
+	
+			return Pattern.compile(builder.toString());
+		}
+		else {
+			return Pattern.compile(glob);
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java
new file mode 100644
index 0000000..00de2b9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import java.io.File;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class PathHelper {
+
+	public static void scanFiles(List<IPath> paths, IPath path, String pattern, boolean recurse) {
+		Pattern p = GlobCompiler.compile(pattern);
+		
+		for ( File f : path.toFile().listFiles() ) {
+			if ( f.isDirectory() && recurse ) {
+				scanFiles( paths, new Path(f.getAbsolutePath()), pattern, recurse);
+			}
+			else if ( f.isFile() && p.matcher(f.getName()).matches() ) {
+				paths.add( new Path(f.getAbsolutePath()) );
+			}
+		}
+	}
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java
new file mode 100644
index 0000000..6f379d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+
+public final class SigilUtils {
+	private SigilUtils () {
+	}
+	
+	public static boolean isResourceType(IResource resource, String type) {
+		IContentType[] types = Platform.getContentTypeManager().findContentTypesFor(resource.getName());
+		for (IContentType contentType : types) {
+			if(contentType.getId().equals(type)) {
+				return true;
+			}
+		}
+		return false;
+	}
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java
new file mode 100644
index 0000000..ce6ad86
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.cauldron.sigil.utils.SigilUtils;
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class EditorPropertyTester extends PropertyTester {
+
+	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+		IWorkbenchPart part = (IWorkbenchPart) receiver;
+
+		boolean result = false;
+		
+		if(part instanceof IEditorPart) {
+			IEditorInput input = ((IEditorPart) part).getEditorInput();
+			if(input instanceof IFileEditorInput) {
+				IFile file = ((IFileEditorInput) input).getFile();
+				
+				if("isEditorOfType".equals(property)) {
+					result = SigilUtils.isResourceType(file, (String) expectedValue);
+				}
+			}
+		}
+		
+		return result;
+	}
+
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java
new file mode 100644
index 0000000..b0dceee
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class PartKindPropertyTester extends PropertyTester{
+
+	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+		IWorkbenchPart part = (IWorkbenchPart) receiver;
+		
+		Object value;
+		if("partKind".equals(property)) {
+			if(part instanceof IEditorPart) {
+				value = "editor";
+			} else if(part instanceof IViewPart) {
+				value = "view";
+			} else {
+				value = null;
+			}
+		} else if("partId".equals(property)) {
+			value = part.getSite().getId();
+		} else {
+			value = null;
+		}
+		
+		return expectedValue.equals(value);
+	}
+	
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java
new file mode 100644
index 0000000..efc32e3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+
+public class ResourceTypePropertyTester extends PropertyTester {
+
+	public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+		if (!(receiver instanceof IResource)) {
+			return false;
+		}
+
+		boolean result = false;
+
+		IResource resource = (IResource) receiver;
+		if ("isResourceOfType".equals(property)) {
+			IContentType[] types = Platform.getContentTypeManager().findContentTypesFor(
+					resource.getName());
+
+			for (IContentType type : types) {
+				if (type.getId().equals(expectedValue)) {
+					result = true;
+					break;
+				}
+			}
+		}
+
+		return result;
+	}
+
+}
diff --git a/sigil/sigil-builder/.classpath b/sigil/sigil-builder/.classpath
new file mode 100644
index 0000000..5805c94
--- /dev/null
+++ b/sigil/sigil-builder/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="build-libs/classes" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="build-libs/ant.jar"/>
+	<classpathentry kind="lib" path="build-libs/osgi.core.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/sigil-builder/.project b/sigil/sigil-builder/.project
new file mode 100644
index 0000000..646a5cd
--- /dev/null
+++ b/sigil/sigil-builder/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>sigil-builder</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/sigil/sigil-builder/build.xml b/sigil/sigil-builder/build.xml
new file mode 100644
index 0000000..4161e54
--- /dev/null
+++ b/sigil/sigil-builder/build.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project name="sigil-builder" default="build">
+  <property name="user.props" value="user.properties" />
+  <property file="${user.props}"/>
+  <property file="project.properties"/>
+  <property file="download.properties"/>
+
+  <property name="error.internal.target" value="Do not call this target directly"/>
+
+  <property name="eclipse.sdk" value="${eclipse.install.dir}"/>
+  <property name="extras.sdk" value="build-libs/target-platform-extras"/>
+
+  <condition property="isUnix">
+      <os family="unix"/>
+  </condition>
+
+  <!-- PUBLIC TARGETS -->
+
+  <target name="build"
+    depends="clean-tmp, target-platform, pdebuild"/>
+
+  <target name="clean-tmp">
+    <delete dir="target/tmp" />
+  </target>
+  
+  <target name="clean">
+    <delete dir="target"/>
+    <delete dir="build-libs/classes"/>
+  </target>
+
+  <!-- create target platform -->
+  <target name="target-platform" depends="rsync-platform, copy-platform"/>
+
+  <target name="rsync-platform" if="isUnix">
+      <mkdir dir="${target.platform}/eclipse" />
+      <exec executable="rsync" append="true"
+        output="${target.platform}/rsync.log" >
+	<arg line="-av --delete ${eclipse.sdk}/ ${extras.sdk}/ ${target.platform}/eclipse" />
+      </exec>
+  </target>
+
+  <target name="copy-platform" unless="isUnix">
+      <mkdir dir="${target.platform}/eclipse" />
+      <copy todir="${target.platform}/eclipse">
+	<fileset dir="${eclipse.sdk}"/>
+	<fileset dir="${extras.sdk}" />
+      </copy>
+  </target>
+
+  <target name="init">
+    <mkdir dir="target"/>
+    <mkdir dir="target/features"/>
+    <mkdir dir="target/plugins"/>
+  </target>
+
+  <target name="custom.tasks">
+    <mkdir dir="build-libs/classes"/>
+    <javac srcdir="src" destdir="build-libs/classes" debug="on">
+      <classpath>
+        <fileset dir="build-libs">
+          <include name="osgi.core.jar"/>
+        </fileset>
+      </classpath>
+    </javac>
+    <taskdef name="findbundle" classname="org.cauldron.sigil.build.FindBundlesTask">
+      <classpath>
+        <pathelement location="build-libs/osgi.core.jar"/>
+        <pathelement location="build-libs/classes"/>
+      </classpath>
+    </taskdef>
+    <taskdef name="siteInsertFeatures" classname="org.cauldron.sigil.build.SiteInsertFeatures">
+      <classpath>
+        <pathelement location="build-libs/classes"/>
+      </classpath>
+    </taskdef>
+  </target>
+
+  <target name="findbundles" depends="custom.tasks">
+    <!-- Find the Equinox launcher JAR -->
+    <echo>${eclipse.install.dir}</echo>
+    <findbundle dir="${eclipse.install.dir}/plugins" symbolicname="org.eclipse.equinox.launcher" property="eclipse.launcher.version"/>
+    <property name="eclipse.launcher.jar" location="${eclipse.install.dir}/plugins/org.eclipse.equinox.launcher_${eclipse.launcher.version}.jar"/>
+
+    <!-- Find the PDE Build scripts -->
+    <findbundle dir="${eclipse.install.dir}/plugins" symbolicname="org.eclipse.pde.build" property="eclipse.pdebuild.version"/>
+    <property name="eclipse.productBuild.xml" location="${eclipse.install.dir}/plugins/org.eclipse.pde.build_${eclipse.pdebuild.version}/scripts/productBuild/productBuild.xml"/>
+    <property name="eclipse.featureBuild.xml" location="${eclipse.install.dir}/plugins/org.eclipse.pde.build_${eclipse.pdebuild.version}/scripts/build.xml"/>
+  </target>
+
+  <!-- Unused
+  <target name="download" depends="init">
+    <mkdir dir="${target.platform}"/>
+
+    <echo message="Downloading platform runtime"/>
+    <get dest="${target.platform}/platform-runtime.zip"
+       src="${download.base}/${download.dir}/${download.platform.runtime}" verbose="true"/>
+
+    <echo message="Downloading RCP delta pack"/>
+    <get dest="${target.platform}/deltapack.zip"
+       src="${download.base}/${download.dir}/${download.deltapack}" verbose="true"/>
+
+    <echo message="Downloading CVS client"/>
+    <get dest="${target.platform}/cvsclient.zip"
+       src="${download.base}/${download.dir}/${download.cvsclient}" verbose="true"/>
+
+    <unzip src="${target.platform}/platform-runtime.zip" dest="${target.platform}" overwrite="true"/>
+    <unzip src="${target.platform}/deltapack.zip" dest="${target.platform}" overwrite="true"/>
+    <unzip src="${target.platform}/cvsclient.zip" dest="${target.platform}" overwrite="true"/>
+  </target>
+  -->
+
+  <target name="copy.projects" depends="init">
+    <foreach list="${plugins}" param="plugin.id"  target="__copy.plugin"/>
+    <foreach list="${features}" param="feature.id" target="__copy.feature"/>
+  </target>
+
+  <target name="copy.properties" depends="init">
+    <copy file="builder/template.build.properties" tofile="builder/build.properties" overwrite="true"/>
+    <pathconvert property="basedir.unix" targetos="unix">
+      <path location="${basedir}"/>
+    </pathconvert>
+    <replace file="builder/build.properties">
+      <replacefilter token="@@WARNING_TEXT" value="Warning! This file is auto-generated. Changes will be overwritten!"/>
+      <replacefilter token="@@BUILD_PROJECT_DIR" value="${basedir.unix}"/>
+      <replacefilter token="@@TARGET_PLATFORM" value="${target.platform}"/>
+    </replace>
+  </target>
+
+  <!-- Unused
+  <target name="copy.deps">
+    <copy todir="${target.platform}/eclipse/plugins">
+      <fileset dir="target-libs" includes="*.jar"/>
+    </copy>
+  </target>
+  -->
+
+  <target name="pdebuild" depends="findbundles,copy.projects,copy.properties">
+    <java jar="${eclipse.launcher.jar}" fork="true" failonerror="true">
+      <sysproperty key="builder" path="${basedir}/builder"/>
+      <arg line="-application org.eclipse.ant.core.antRunner"/>
+      <arg line="-buildfile ${eclipse.featureBuild.xml}"/>
+    </java>
+  </target>
+  
+  <target name="new.updateSite">
+    <copy file="site.xml" todir="${updateSiteDir}"/>
+    <antcall target="copy.updateSite" />
+  </target>
+
+  <target name="copy.updateSite" depends="custom.tasks">
+    <copy todir="${updateSiteDir}">
+      <fileset dir="target/tmp/eclipse" includes="**/*.jar"/>
+    </copy>
+    <property file="target/finalFeaturesVersions.properties" prefix="featureVersions"/>
+    <siteInsertFeatures sitexmlfile="${updateSiteDir}/site.xml"
+      features="${features}"
+      versionPropPrefix="featureVersions"
+      categoryPropPrefix="category"/>
+  </target>
+
+  <!-- INTERNAL TARGETS -->
+  <target name="__copy.plugin">
+    <fail unless="plugin.id" message="${error.internal.target}"/>
+    <echo message="Copying plugin ${plugin.id}"/>
+    <antcall target="__copy.artifact">
+      <param name="artifact.id" value="${plugin.id}"/>
+      <param name="artifact.type" value="plugin"/>
+    </antcall>
+  </target>
+
+  <target name="__copy.feature">
+    <fail unless="feature.id" message="${error.internal.target}"/>
+    <antcall target="__copy.artifact">
+      <param name="artifact.id" value="${feature.id}"/>
+      <param name="artifact.type" value="feature"/>
+    </antcall>
+    
+    <replace file="target/features/${feature.id}/feature.xml">
+      <replacefilter token="http://replace.with.real.url" value="${updateUrl}"/>
+    </replace>
+  </target>
+
+  <target name="__copy.artifact">
+    <fail unless="artifact.id" message="${error.internal.target}"/>
+    <fail unless="artifact.type" message="${error.internal.target}"/>
+
+    <echo message="Copying from ${source.dir}/${artifact.id} to target/${artifact.type}s"/>
+    <copy todir="target/${artifact.type}s">
+      <fileset dir="${source.dir}">
+        <exclude name="${artifact.id}/bin/**"/>
+        <exclude name="${artifact.id}/build/**"/>
+        <exclude name="${artifact.id}/**/*.class"/>
+        <include name="${artifact.id}/**"/>
+      </fileset>
+    </copy>
+  </target>
+
+  <target name="__nodefault">
+    <fail message="There is no default target"/>
+  </target>
+
+  <!-- TASK DEFINITIONS -->
+  <taskdef resource="net/sf/antcontrib/antlib.xml">
+    <classpath>
+      <pathelement location="build-libs/ant-contrib/ant-contrib-1.0b3.jar"/>
+    </classpath>
+  </taskdef>
+
+</project>
diff --git a/sigil/sigil-builder/builder/template.build.properties b/sigil/sigil-builder/builder/template.build.properties
new file mode 100644
index 0000000..a097442
--- /dev/null
+++ b/sigil/sigil-builder/builder/template.build.properties
@@ -0,0 +1,215 @@
+###############################################################################
+#
+# @@WARNING_TEXT
+#
+############# WHAT SHOULD WE BUILD? #############
+
+#To build a Product
+#product=/org.blah.blah/blah.product
+#runPackager=true
+
+#To build a Feature
+topLevelElementId=org.cauldron.sigil.all.feature
+topLevelElementType=feature
+
+#The prefix that will be used in the generated archive.
+archivePrefix=eclipse
+
+# The location underwhich all of the build output will be collected.
+collectingFolder=${archivePrefix}
+
+# The list of {os, ws, arch} configurations to build.  This 
+# value is a '&' separated list of ',' separate triples.  For example, 
+#     configs=win32,win32,x86 & linux,motif,x86
+# By default the value is *,*,*
+configs=\
+	*, *, *
+#	win32, win32, x86
+#	macosx, carbon, ppc &\
+#	linux, gtk, ppc &\
+#	linux, gtk, x86 & \
+#	linux, gtk, x86_64 & \
+#	linux, motif, x86 & \
+#	solaris, motif, sparc & \
+#	solaris, gtk, sparc & \
+#	aix, motif, ppc & \
+#	hpux, motif, PA_RISC & \
+#	macosx, carbon, ppc
+
+# By default PDE creates one archive (result) per entry listed in the configs property.
+# Setting this value to try will cause PDE to only create one output containing all 
+# artifacts for all the platforms listed in the configs property.
+#groupConfigurations=true
+
+#The format of the archive. By default a zip is created using antZip.
+#The list can only contain the configuration for which the desired format is different than zip.
+archivesFormat=*,*,* - folder
+#archivesFormat=win32, win32, x86 - antZip& \
+#	linux, gtk, ppc - antZip &\
+#    linux, gtk, x86 - antZip& \
+#	linux, gtk, x86_64 - antZip& \
+# linux, motif, x86 - antZip& \
+#	solaris, motif, sparc - antZip& \
+#	solaris, gtk, sparc - antZip& \
+#	aix, motif, ppc - antZip& \
+#	hpux, motif, PA_RISC - antZip& \
+#	macosx, carbon, ppc - antZip
+	
+#Set to true if you want the output to be ready for an update jar (no site.xml generated)
+outputUpdateJars=true
+
+#Set to true for Jnlp generation
+#codebase should be a URL that will be used as the root of all relative URLs in the output.
+#generateJnlp=false
+#jnlp.codebase=<codebase url>
+#jnlp.j2se=<j2se version>
+#jnlp.locale=<a locale>
+#jnlp.generateOfflineAllowed=true or false generate <offlineAllowed/> attribute in the generated features
+#jnlp.configs=${configs}			#uncomment to filter the content of the generated jnlp files based on the configuration being built
+
+#Set to true if you want to sign jars
+#signJars=false
+#sign.alias=<alias>
+#sign.keystore=<keystore location>
+#sign.storepass=<keystore password>
+
+#Arguments to send to the zip executable
+zipargs=
+
+#Arguments to send to the tar executable
+tarargs=
+
+#Control the creation of a file containing the version included in each configuration - on by default 
+#generateVersionsLists=false
+
+############## BUILD NAMING CONTROL ################
+# The directory into which the build elements are fetched and where
+# the build takes place.
+buildProjectDirectory=@@BUILD_PROJECT_DIR
+buildDirectory=${buildProjectDirectory}/target
+
+# Type of build.  Used in naming the build output.  Typically this value is
+# one of I, N, M, S, ...
+buildType=N
+
+# ID of the build.  Used in naming the build output.
+buildId=Sigil
+
+# Label for the build.  Used in naming the build output
+buildLabel=${buildType}.${buildId}
+
+# Timestamp for the build.  Used in naming the build output
+timestamp=007
+
+#The value to be used for the qualifier of a plugin or feature when you want to override the value computed by pde.
+#The value will only be applied to plugin or features indicating build.properties, qualifier = context 
+#forceContextQualifier=<the value for the qualifier>
+
+#Enable / disable the generation of a suffix for the features that use .qualifier. 
+#The generated suffix is computed according to the content of the feature   
+#generateFeatureVersionSuffix=true
+
+############# BASE CONTROL #############
+# Settings for the base Eclipse components and Java class libraries 
+# against which you are building.
+# Base location for anything the build needs to compile against.  For example,
+# in most RCP app or a plug-in,  the baseLocation should be the location of a previously
+# installed Eclipse against which the application or plug-in code will be compiled and the RCP delta pack.
+
+base=@@TARGET_PLATFORM
+baseLocation=${base}/eclipse
+#Os/Ws/Arch/nl of the eclipse specified by baseLocation
+baseos=win32
+basews=win32
+basearch=x86
+
+#this property indicates whether you want the set of plug-ins and features to be considered during the build to be limited to the ones reachable from the features / plugins being built
+filteredDependencyCheck=false
+
+#this property indicates whether the resolution should be done in development mode (i.e. ignore multiple bundles with singletons)
+resolution.devMode=false
+
+#pluginPath is a list of locations in which to find plugins and features.  This list is separated by the platform file separator (; or :)
+#a location is one of:  
+#- the location of the jar or folder that is the plugin or feature : /path/to/foo.jar or /path/to/foo
+#- a directory that contains a /plugins or /features subdirectory
+#- the location of a feature.xml, or for 2.1 style plugins, the plugin.xml or fragment.xml
+#pluginPath=
+
+skipBase=true
+eclipseURL=http://www.eclipse.org/downloads/
+eclipseBuildId=3.3RC2
+eclipseBaseURL=${eclipseURL}/eclipse-platform-${eclipseBuildId}-win32.zip
+
+
+############# MAP FILE CONTROL ################
+# This section defines CVS tags to use when fetching the map files from the repository.
+# If you want to fetch the map file from repository / location, change the getMapFiles target in the customTargets.xml
+
+skipMaps=true
+mapsRepo=:pserver:anonymous@example.com/path/to/repo
+mapsRoot=path/to/maps
+mapsCheckoutTag=HEAD
+
+#tagMaps=true
+mapsTagTag=v${buildId}
+
+
+############ REPOSITORY CONTROL ###############
+# This section defines properties parameterizing the repositories where plugins, fragments
+# bundles and features are being obtained from. 
+
+# The tags to use when fetching elements to build.
+# By default thebuilder will use whatever is in the maps.  
+# This value takes the form of a comma separated list of repository identifier (like used in the map files) and the 
+# overriding value
+# For example fetchTag=CVS=HEAD, SVN=v20050101
+# fetchTag=HEAD
+skipFetch=true
+
+
+############# JAVA COMPILER OPTIONS ##############
+# The location of the Java jars to compile against.  Typically the rt.jar for your JDK/JRE
+#bootclasspath=${java.home}/lib/rt.jar
+
+# specific JRE locations to compile against. These values are used to compile bundles specifying a 
+# Bundle-RequiredExecutionEnvironment. Uncomment and set values for environments that you support
+#CDC-1.0/Foundation-1.0= /path/to/rt.jar
+#CDC-1.1/Foundation-1.1=
+#OSGi/Minimum-1.0=
+#OSGi/Minimum-1.1=
+#JRE-1.1=
+#J2SE-1.2=
+#J2SE-1.3=
+#J2SE-1.4=
+#J2SE-1.5=
+#JavaSE-1.6=
+#PersonalJava-1.1=
+#PersonalJava-1.2=
+#CDC-1.0/PersonalBasis-1.0=
+#CDC-1.0/PersonalJava-1.0=
+#CDC-1.1/PersonalBasis-1.1=
+#CDC-1.1/PersonalJava-1.1=
+
+# Specify the output format of the compiler log when eclipse jdt is used
+logExtension=.log
+
+# Whether or not to include debug info in the output jars
+javacDebugInfo=true
+
+# Whether or not to fail the build if there are compiler errors
+javacFailOnError=true
+
+# Enable or disable verbose mode of the compiler
+javacVerbose=true
+
+# Extra arguments for the compiler. These are specific to the java compiler being used.
+#compilerArg=
+
+# Default value for the version of the source code. This value is used when compiling plug-ins that do not set the Bundle-RequiredExecutionEnvironment or set javacSource in build.properties
+javacSource=1.5
+
+# Default value for the version of the byte code targeted. This value is used when compiling plug-ins that do not set the Bundle-RequiredExecutionEnvironment or set javacTarget in build.properties.
+javacTarget=1.5
+
+
diff --git a/sigil/sigil-builder/download.properties b/sigil/sigil-builder/download.properties
new file mode 100644
index 0000000..fd21601
--- /dev/null
+++ b/sigil/sigil-builder/download.properties
@@ -0,0 +1,10 @@
+# For UK mirror service
+download.base=http://www.mirrorservice.org/sites/download.eclipse.org/eclipseMirror/eclipse/downloads/drops/
+
+# 3.3.1.1 Release
+download.dir=R-3.3.1.1-200710231652
+
+#
+download.platform.runtime=eclipse-platform-3.3.1.1-win32.zip
+download.deltapack=eclipse-RCP-3.3.1.1-delta-pack.zip
+download.cvsclient=eclipse-CVS-Client-3.3.1.1.zip
\ No newline at end of file
diff --git a/sigil/sigil-builder/project.properties b/sigil/sigil-builder/project.properties
new file mode 100644
index 0000000..b244e72
--- /dev/null
+++ b/sigil/sigil-builder/project.properties
@@ -0,0 +1,18 @@
+# The list of plugins to be build
+plugins=org.cauldron.sigil.core,\
+	org.cauldron.sigil.obr,\
+	org.cauldron.sigil.search,\
+	org.cauldron.sigil.ui,\
+	org.cauldron.sigil.help,\
+	org.cauldron.sigil.utils,\
+	org.cauldron.bld.core,\
+	org.cauldron.bld.obr
+
+# The list of features to be built
+features=org.cauldron.sigil.all.feature,\
+	org.cauldron.sigil.feature,\
+	org.cauldron.sigil.obr.feature
+
+# Mapping of features to categories in the Update Site
+category.org.cauldron.sigil.feature=Sigil
+category.org.cauldron.sigil.obr.feature=Sigil
diff --git a/sigil/sigil-builder/readme.html b/sigil/sigil-builder/readme.html
new file mode 100644
index 0000000..2427241
--- /dev/null
+++ b/sigil/sigil-builder/readme.html
@@ -0,0 +1,131 @@
+<html>
+<head>
+<title>Sigil Build Instructions</title>
+</head>
+<body>
+
+<h1>Build Instructions</h1>
+
+<h2>1. Install Prerequisites</h2>
+
+<p>A full copy of Eclipse SDK is required to build. This must be
+installed in a directory called <code>eclipse</code>, and the full path
+should not contain any spaces. For example:</p>
+
+<ul>
+	<li><code>C:/eclipse-SDK-3.4/eclipse</code> is GOOD</li>
+	<li><code>C:/eclipse-SDK-3.4/</code> is BAD</li>
+	<li><code>C:/Program Files/Eclipse 3.4/</code> is VERY BAD</li>
+</ul>
+
+<p>This directory (<strong>including</strong> the final <code>eclipse</code>)
+will be referred to as the SDK directory.</p>
+
+<h2>2. Setup the Target Platform</h2>
+
+<p>The target platform is the collection of binary plugins and
+features against which the source projects are built. It is an Eclipse
+installation (with a <code>plugins</code> and <code>features</code>
+directory), and so can be setup by downloading the correct zip or
+tarball from the Eclipse website. Like the SDK directory, the target
+platform must be inside a directory called <code>eclipse</code>. In your
+IDE, you should point PDE at the target platform by opening Preferences
+and navigating to "Plug-in Development" --&gt; "Target Platform". Click
+Browse and select the <code>eclipse</code> directory.</p>
+
+<p>Note that since we are building SDK plugins, the Target Platform
+will be a full SDK (eg "Eclipse Classic SDK"). Therefore we can reuse
+the same SDK directory that we setup in the previous step. However it
+can also be useful to separate these because we might want to use
+different Eclipse versions for each function. For example, we may want
+to use Eclipse 3.4 to build a 3.3-based product.</p>
+
+<h2>3. Create and edit <code>user.properties</code></h2>
+
+<p>User-specific properties are supplied in the file <code>user.properties</code>.
+After checkout this file does not exist, so please copy <code>template.user.properties</code>
+to <code>user.properties</code> and change the following settings:</p>
+
+<ul>
+	<li><code>target.platform</code>: this should be set to the <strong>parent
+	</strong> directory of the <code>eclipse</code> directory which contains the
+	Target Platform.</li>
+	<li><code>eclipse.install.dir</code>: the SDK directory (including
+	final <code>eclipse</code> segment).</li>
+</ul>
+
+<p>Do not check <code>user.properties</code> into version control:
+the settings inside are specific to your machine.</p>
+
+<h2>4. Configure the Update Site URL</h2>
+
+<p>The Update Site URL can be specified by setting the <code>updateUrl</code>
+property in <code>project.properties</code> . This will be copied into
+the <code>feature.xml</code> descriptor for each feature that is part of
+the build. For this to work, the update site URL setting in the <code>feature.xml</code>
+of each source feature project must be set to the following placeholder
+string: <code>http://replace.with.real.url</code></p>
+
+<h2>5. Execute the Build</h2>
+
+<p>Execute a full build by running the <code>pdebuild</code> target
+in ANT, e.g.:</p>
+
+<pre>
+    ant pdebuild
+</pre>
+
+<p>This will generate a directory named <code>target/tmp/eclipse</code>
+which contains a <code>plugins</code> directory for the built plugin
+JARs and a <code>features</code> directory for the built features. If
+the build fails then compilation logs for each plugin can be found in <code>target/N.Sigil/compilelogs/plugins</code>.</p>
+
+<h2>6. Deploy to Update Site</h2>
+
+<p>After the features and plugins are built we can deploy them to an
+update site on the local filesystem. The update site directory must
+already exist with the following layout:</p>
+
+<pre>
+${updateSiteDir}/
+ |--plugins/
+ |--features/
+ \--site.xml
+</pre>
+
+<p>And <code>site.xml</code> must have at least the following
+content:</p>
+
+<pre>
+&lt;site&gt;
+    &lt;category-def name="Sigil" label="Sigil"&gt;
+        &lt;description&gt;Blah blah&lt;/description&gt;
+    &lt;/category-def&gt;
+&lt;/site&gt;
+</pre>
+
+<p>It can also have any number of pre-existing <code>&lt;feature&gt;</code>
+entries as direct children of the <code>&lt;site&gt;</code> element.</p>
+
+<p>Next set the <code>updateSiteDir</code> property in <code>project.properties</code>
+to point at the above directory. Now we can run the deploy target as
+follows:</p>
+
+<pre>
+    ant copy.updateSite
+</pre>
+
+<p>This will copy all of the plugin and feature JARs that were built
+in the previous step into the update site directory. It will also add a
+new entry into <code>site.xml</code> for each feature. Note that the
+update site directory will grow continually. Since the plugins and
+features are generated with a timestamp in their version qualifier (e.g.
+1.2.3.200808091200 for version 1.2.3 built on 9 Aug 2008 at 12 o'clock
+GMT), Eclipse Update Manager will always select the newest version
+available.</p>
+
+<p>NB: the deploy target will overwrite <code>site.xml</code>. A
+backup will be saved in <code>site.xml.bak</code> but this backup will
+be overwritten next time the build is run.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/sigil-builder/server-nightly.properties b/sigil/sigil-builder/server-nightly.properties
new file mode 100644
index 0000000..c049a4a
--- /dev/null
+++ b/sigil/sigil-builder/server-nightly.properties
@@ -0,0 +1,6 @@
+eclipse.install.dir=/opt/eclipse-SDK-3.4/eclipse
+source.dir=${basedir}/..
+target.platform=target/target-platform
+updateSiteDir=target/update-site
+# updateUrl is over-ridden from Hudson
+updateUrl=http://sigil.codecauldron.org/update-site-daily
diff --git a/sigil/sigil-builder/site.xml b/sigil/sigil-builder/site.xml
new file mode 100644
index 0000000..5ad74f3
--- /dev/null
+++ b/sigil/sigil-builder/site.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+    http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<site>
+   <category-def name="Sigil" label="Sigil Core">
+      <description>
+         Sigil is an SDK for developing applications to be deployed on the Newton framework (http://newton.codecauldron.org)
+      </description>
+   </category-def>
+</site>
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java
new file mode 100644
index 0000000..6f4378a
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * 
+ */
+package org.cauldron.sigil.build;
+
+class Feature {
+	String id, version, url;
+	String[] categories;
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java
new file mode 100644
index 0000000..0f7b4a2
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.osgi.framework.Version;
+
+public class FindBundlesTask extends Task {
+
+	private File dir;
+	private String symbolicName;
+	private String property;
+
+	public File getDir() {
+		return dir;
+	}
+
+	public void setDir(File dir) {
+		this.dir = dir;
+	}
+
+	public String getSymbolicName() {
+		return symbolicName;
+	}
+
+	public void setSymbolicName(String symbolicName) {
+		this.symbolicName = symbolicName;
+	}
+
+	public String getProperty() {
+		return property;
+	}
+
+	public void setProperty(String property) {
+		this.property = property;
+	}
+
+	@Override
+	public void execute() throws BuildException {
+		System.out.println("Searching " + dir + " for bundle '" + symbolicName + "'");
+		final String prefix = symbolicName + "_";
+		String[] files = dir.list(new FilenameFilter() {
+			public boolean accept(File dir, String name) {
+				return name.startsWith(prefix);
+			}
+		});
+		if (files == null)
+		    files = new String[0];
+
+		System.out.println("Found " + files.length + " file(s) starting with " + symbolicName);
+
+		Version highest = null;
+		for (String filename : files) {
+			System.out.println("Testing " + filename);
+			// Drop the prefix
+			int startIndex = prefix.length();
+
+			// Drop the ".jar" suffix if present
+			int endIndex = filename.length();
+			if (filename.toLowerCase().endsWith(".jar")) {
+				endIndex -= 4;
+			}
+
+			String versionString = filename.substring(startIndex, endIndex);
+			System.out.println("Version string is '" + versionString + "'");
+
+			Version version = new Version(versionString);
+			if (highest == null || version.compareTo(highest) > 0) {
+				highest = version;
+			}
+		}
+
+		if (highest == null) {
+			throw new BuildException("No matches for symbolic name '"
+					+ symbolicName + "'");
+		}
+
+		getProject().setNewProperty(property, highest.toString());
+	}
+
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java
new file mode 100644
index 0000000..bf710a7
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.util.List;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+class SiteInsertFeatureContentHandler implements ContentHandler {
+	
+	private final ContentHandler output;
+	private final List<org.cauldron.sigil.build.Feature> featureList;
+
+	public SiteInsertFeatureContentHandler(ContentHandler output,
+			List<Feature> featureList) {
+		this.output = output;
+		this.featureList = featureList;
+	}
+
+	public void characters(char[] ch, int start, int length) throws SAXException {
+		output.characters(ch, start, length);
+	}
+
+	public void endDocument() throws SAXException {
+		output.endDocument();
+	}
+
+	public void endElement(String uri, String localName, String name) throws SAXException {
+		output.endElement(uri, localName, name);
+	}
+
+	public void endPrefixMapping(String prefix) throws SAXException {
+		output.endPrefixMapping(prefix);
+	}
+
+	public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+		//output.ignorableWhitespace(ch, start, length);
+	}
+
+	public void processingInstruction(String target, String data) throws SAXException {
+		output.processingInstruction(target, data);
+	}
+
+	public void setDocumentLocator(Locator locator) {
+		output.setDocumentLocator(locator);
+	}
+
+	public void skippedEntity(String name) throws SAXException {
+		output.skippedEntity(name);
+	}
+
+	public void startDocument() throws SAXException {
+		output.startDocument();
+	}
+
+	public void startElement(String uri, String localName, String name, Attributes atts)
+			throws SAXException {
+		output.startElement(uri, localName, name, atts);
+		
+		if("site".equals(name)) {
+			for (Feature feature : featureList) {
+				AttributesImpl featureAtts = new AttributesImpl();
+				featureAtts.addAttribute("", "", "url", "CDATA", feature.url);
+				featureAtts.addAttribute("", "", "id", "CDATA", feature.id);
+				featureAtts.addAttribute("", "", "version", "CDATA", feature.version);
+				output.startElement("", "", "feature", featureAtts);
+				
+				for (int i = 0; i < feature.categories.length; i++) {
+					AttributesImpl categoryAtts = new AttributesImpl();
+					categoryAtts.addAttribute("", "", "name", "CDATA", feature.categories[i]);
+					output.startElement("", "", "category", categoryAtts);
+					output.endElement("", "", "category");
+				}
+				
+				output.endElement("", "", "feature");
+			}
+		}
+	}
+
+	public void startPrefixMapping(String prefix, String uri) throws SAXException {
+		output.startPrefixMapping(prefix, uri);
+	}
+
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java
new file mode 100644
index 0000000..9d09008
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+public class SiteInsertFeatures extends Task {
+
+	private File siteXmlFile;
+	private String features;
+	private String versionPropPrefix;
+	private String categoryPropPrefix;
+	
+	public File getSiteXmlFile() {
+		return siteXmlFile;
+	}
+	public void setSiteXmlFile(File siteXmlFile) {
+		this.siteXmlFile = siteXmlFile;
+	}
+	public String getFeatures() {
+		return features;
+	}
+	public void setFeatures(String features) {
+		this.features = features;
+	}
+	public String getVersionPropPrefix() {
+		return versionPropPrefix;
+	}
+	public void setVersionPropPrefix(String versionPropPrefix) {
+		this.versionPropPrefix = versionPropPrefix;
+	}
+	public String getCategoryPropPrefix() {
+		return categoryPropPrefix;
+	}
+	public void setCategoryPropPrefix(String categoryPropPrefix) {
+		this.categoryPropPrefix = categoryPropPrefix;
+	}
+	
+	@Override
+	public void execute() throws BuildException {
+		Project project = getProject();
+		
+		List<Feature> featureList = new ArrayList<Feature>(); 
+		StringTokenizer tokenizer = new StringTokenizer(features, ",");
+		while(tokenizer.hasMoreTokens()) {
+			Feature feature = new Feature();
+			feature.id = tokenizer.nextToken().trim();
+			
+			// Find the version property
+			String versionProp;
+			if(versionPropPrefix == null) {
+				versionProp = feature.id;
+			} else {
+				versionProp = versionPropPrefix + "." + feature.id;
+			}
+			feature.version = project.getProperty(versionProp);
+			
+			// Find the categories for this feature
+			feature.categories = new String[0];
+			if(categoryPropPrefix != null) {
+				String categoriesStr = project.getProperty(categoryPropPrefix + "." + feature.id);
+				if(categoriesStr != null) {
+					StringTokenizer categoriesTokenizer = new StringTokenizer(categoriesStr, ",");
+					feature.categories = new String[categoriesTokenizer.countTokens()];
+					for(int i=0; i<feature.categories.length; i++) {
+						feature.categories[i] = categoriesTokenizer.nextToken();
+					}
+				}
+			}
+
+			if(feature.version != null) {
+				feature.url = "features/" + feature.id + "_" + feature.version + ".jar";
+				featureList.add(feature);
+			} else {
+				System.out.println("Skipping feature " + feature.id);
+			}
+		}
+		
+		if(!siteXmlFile.isFile()) {
+			throw new BuildException(siteXmlFile + " does not exist or is not a normal file");
+		}
+		try {
+			// Generate new XML into a temporary file
+			File tempFile = File.createTempFile("tmp", ".xml", siteXmlFile.getParentFile());
+			tempFile.deleteOnExit();
+
+			SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+			TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+			transformerHandler.setResult(new StreamResult(tempFile));
+			
+			SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+			SAXParser parser = parserFactory.newSAXParser();
+			
+			SiteInsertFeatureContentHandler contentHandler = new SiteInsertFeatureContentHandler(transformerHandler, featureList);
+			
+			XMLReader reader = parser.getXMLReader();
+			reader.setContentHandler(contentHandler);
+			reader.parse(new InputSource(new FileInputStream(siteXmlFile)));
+			
+			// Backup original file
+			File backup = new File(siteXmlFile.getParentFile(), siteXmlFile.getName() + ".bak");
+			copyFile(siteXmlFile, backup);
+			
+			// Replace original file
+			copyFile(tempFile, siteXmlFile);
+			
+		} catch (IOException e) {
+			throw new BuildException(e);
+		} catch (TransformerConfigurationException e) {
+			throw new BuildException(e);
+		} catch (IllegalArgumentException e) {
+			throw new BuildException(e);
+		} catch (TransformerFactoryConfigurationError e) {
+			throw new BuildException(e);
+		} catch (ParserConfigurationException e) {
+			throw new BuildException(e);
+		} catch (SAXException e) {
+			throw new BuildException(e);
+		}
+	}
+	
+	private void copyFile(File source, File dest) throws IOException {
+		FileInputStream in = null;
+		FileOutputStream out = null;
+		try {
+			in = new FileInputStream(source);
+			out = new FileOutputStream(dest);
+			
+			byte[] buffer = new byte[1024];
+			
+			int read;
+			while((read = in.read(buffer, 0, 1024)) > -1) {
+				out.write(buffer, 0, read);
+			}
+		} finally {
+			try { if(in != null) in.close(); } catch(IOException e) {}
+			try { if(out != null) out.close(); } catch(IOException e) {}
+		}
+		
+	}
+}
diff --git a/sigil/sigil-builder/template.user.properties b/sigil/sigil-builder/template.user.properties
new file mode 100644
index 0000000..ed14a54
--- /dev/null
+++ b/sigil/sigil-builder/template.user.properties
@@ -0,0 +1,14 @@
+#
+source.dir=${basedir}/..
+
+#
+target.platform=/path/to/target
+
+# path to eclipse sdk
+eclipse.install.dir=/path/to/sdk/eclipse
+
+# The URL of the Update Site, for insertion into each feature.xml
+updateUrl=http://sigil.codecauldron.org/update-site-nightly
+
+# The path to an Update Site directory on the local filesystem
+updateSiteDir=/opt/development/sigil-trunk/update-site-nightly