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/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 );
+		}
+	}
+
+}