Initial commit of Sigil contribution. (FELIX-1142)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@793581 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/LICENCE b/sigil/LICENCE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/sigil/LICENCE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/sigil/bld-ivy/.classpath b/sigil/bld-ivy/.classpath
new file mode 100644
index 0000000..f03d6f0
--- /dev/null
+++ b/sigil/bld-ivy/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.cauldron.bld.core"/>
+ <classpathentry kind="lib" path="lib/runtime/equinox.common.jar"/>
+ <classpathentry kind="lib" path="lib/runtime/osgi.core.jar"/>
+ <classpathentry kind="lib" path="lib/compile/ant.jar"/>
+ <classpathentry kind="lib" path="lib/compile/ivy-2.0.0-rc1.jar"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/sigil/bld-ivy/.project b/sigil/bld-ivy/.project
new file mode 100644
index 0000000..4ccb600
--- /dev/null
+++ b/sigil/bld-ivy/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>bld-ivy</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/bld-ivy/build.xml b/sigil/bld-ivy/build.xml
new file mode 100644
index 0000000..b9f9dbf
--- /dev/null
+++ b/sigil/bld-ivy/build.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns:ivy="antlib:org.apache.ivy.ant" name="bld-ivy" default="jar">
+
+ <!-- buildVersion is overridden by Hudson -->
+ <property name="buildVersion" value="0.8.0-dev" />
+ <property name="version" value="${buildVersion}" />
+
+ <property name="name" value="sigil-ivy-plugin" />
+ <property name="name.jar" value="${name}.jar" />
+ <property name="name-version.jar" value="${name}-${version}.jar" />
+
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="target.dir" value="${basedir}/target" />
+ <property name="classes.dir" value="${basedir}/target/classes" />
+
+ <property name="java-src.dir" value="${basedir}/src" />
+
+ <property name="sigil-plugins.dir" location="../sigil-builder/target/tmp/eclipse/plugins" />
+ <property name="bnd-plugins.dir" location="../org.cauldron.bld.core/lib" />
+
+ <target name="init" depends="find-plugins">
+
+ <path id="run.classpath">
+ <pathelement location="${classes.dir}" />
+ <pathelement location="${bld-core.jar}" />
+ <pathelement location="${bld-obr.jar}" />
+ <pathelement location="${equinox-common.jar}" />
+ <pathelement location="${osgi-core.jar}" />
+ </path>
+
+ <path id="compile.classpath">
+ <path refid="run.classpath"/>
+ <fileset dir="${lib.dir}/compile">
+ <include name="*.jar" />
+ </fileset>
+ </path>
+
+ </target>
+
+ <target name="mkdirs">
+ <mkdir dir="${target.dir}" />
+ <mkdir dir="${classes.dir}" />
+ </target>
+
+ <target name="find-plugins" depends="mkdirs">
+ <property name="osgi-core.jar" value="${lib.dir}/runtime/osgi.core.jar"/>
+ <property name="equinox-common.jar" value="${lib.dir}/runtime/equinox.common.jar"/>
+
+ <fileset id="bld-core" dir="${sigil-plugins.dir}"
+ includes="org.cauldron.bld.core_*.jar" />
+ <property name="bld-core" refid="bld-core"/>
+ <property name="bld-core.jar" location="${sigil-plugins.dir}/${bld-core}"/>
+
+ <fileset id="bld-obr" dir="${sigil-plugins.dir}"
+ includes="org.cauldron.bld.obr*.jar" />
+ <property name="bld-obr" refid="bld-obr"/>
+ <property name="bld-obr.jar" location="${sigil-plugins.dir}/${bld-obr}"/>
+ </target>
+
+ <target name="clean">
+ <delete dir="${target.dir}" />
+ </target>
+
+ <target name="compile" depends="init">
+ <javac debug="true" debuglevel="source,lines,vars" source="1.5" target="1.5" fork="true" destdir="${classes.dir}" srcdir="${java-src.dir}">
+ <classpath refid="compile.classpath" />
+ </javac>
+ </target>
+
+ <target name="dist" depends="jar">
+ <zip destfile="${target.dir}/sigil-${version}.zip">
+ <zipfileset dir="example" prefix="sigil-${version}/example"
+ excludes="**/build/**, **/lib/**, **/ivy-cache/**, **/repository/**"/>
+ <zipfileset dir="target" prefix="sigil-${version}/lib"
+ includes="${name.jar}, bndlib.jar"/>
+ </zip>
+
+ <copy file="${target.dir}/${name.jar}" tofile="${target.dir}/${name-version.jar}"/>
+ </target>
+
+ <target name="jar" depends="compile, taskdefs">
+ <bnd
+ classpath="${toString:run.classpath}"
+ files="sigil-ivy-plugin.bnd"
+ output="${target.dir}/${name.jar}"
+ eclipse="false"
+ failok="false"
+ exceptions="true" />
+
+ <copy todir="${target.dir}">
+ <fileset dir="${bnd-plugins.dir}" includes="bndlib.jar"/>
+ </copy>
+ </target>
+
+ <target name="taskdefs">
+ <taskdef resource="aQute/bnd/ant/taskdef.properties"
+ classpath="${lib.dir}/ant/bnd-0.0.312.jar"/>
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/dependence/README b/sigil/bld-ivy/example/dependence/README
new file mode 100644
index 0000000..09ecfa8
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/README
@@ -0,0 +1,19 @@
+# http://sigil.codecauldron.org
+
+This example is based on the Ivy Tutorial project dependencies example:
+http://ant.apache.org/ivy/history/latest-milestone/tutorial/dependence.html
+
+Dependencies are resolved using an OBR index for the Spring repository,
+hosted at sigil.codecauldron.org.
+
+1. set ivy.jar location in settings/build.properties
+
+2. build dependee
+$ cd dependee
+$ ant jar
+$ ant publish
+
+3. build depender
+$ cd ../depender
+$ ant
+
diff --git a/sigil/bld-ivy/example/dependence/build.xml b/sigil/bld-ivy/example/dependence/build.xml
new file mode 100644
index 0000000..ccc460d
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/build.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project default="clean">
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean directories">
+ <delete includeemptydirs="true">
+ <fileset dir="settings">
+ <exclude name="ivysettings.*" />
+ <exclude name="sigil-repos.properties" />
+ </fileset>
+ </delete>
+ <ant dir="dependee" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ </target>
+
+ <!-- =================================
+ target: all
+ ================================= -->
+ <target name="all" depends="clean" description="--> make the whole example of dependency">
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/dependence/dependee/build.xml b/sigil/bld-ivy/example/dependence/dependee/build.xml
new file mode 100644
index 0000000..4eab164
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/build.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="dependee" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <!-- =================================
+ target: init
+ ================================= -->
+ <target name="init">
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar}"/>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="init"
+ description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="standalone.Main"/>
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="compile" description="--> make a jar file for this project">
+ <propertyfile file="${classes.dir}/version.properties">
+ <entry key="version" type="int" operation="+" default="0" />
+ </propertyfile>
+ <property file="${classes.dir}/version.properties" />
+ <!--
+ <jar destfile="${build.dir}/${ant.project.name}.jar">
+ <fileset dir="${classes.dir}" />
+ </jar>
+ -->
+ <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+ classpathref="run.path.id" />
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="jar" description="--> publish this project in the ivy repository">
+ <property name="revision" value="${version}"/>
+ <delete file="${build.dir}/ivy.xml"/>
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="projects"
+ pubrevision="${revision}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${revision}" />
+ </target>
+
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name="sigil.properties" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/sigil/bld-ivy/example/dependence/dependee/ivy.xml b/sigil/bld-ivy/example/dependence/dependee/ivy.xml
new file mode 100644
index 0000000..631338a
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="dependee"/>
+ <dependencies>
+ <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/dependence/dependee/sigil.properties b/sigil/bld-ivy/example/dependence/dependee/sigil.properties
new file mode 100644
index 0000000..619a841
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/sigil.properties
@@ -0,0 +1,24 @@
+# dependee sigil.properties
+
+version: 1.0.0
+
+-bundles: dependee
+
+-exports: standalone
+
+-imports: \
+ org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+# add these to -imports to test the resolution process
+# javax.servlet;version="(2.4,3.0]", \
+# org.apache.log4j;version="[1.2.14,1.3)", \
+# org.apache.commons.logging, \
+
+# this is a require-bundle dependency
+#-requires: \
+# com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+-resources: \
+ version.properties
+
+# end
diff --git a/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java b/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java
new file mode 100644
index 0000000..1b7398c
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/dependee/src/standalone/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package standalone;
+
+import java.util.Properties;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+ /**
+ * Returns the version of the project
+ * @return a string representation of the version, null if the version could not be retreived
+ */
+ public static String getVersion() {
+ Properties p = new Properties();
+ try {
+ p.load(Main.class.getResourceAsStream("/version.properties"));
+ String version = p.getProperty("version");
+ if (version != null) {
+ return String.valueOf(Integer.parseInt(version));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Return the same string with all words capitalized.
+ * @param str the string conatining the words to capitalize
+ * @return null if the string was null, the string with all words capitalized otherwise
+ */
+ public static String capitalizeWords(String str) {
+ System.out.println(" [" + Main.class.getName() + "] capitalizing string \""
+ + str + "\" using " + WordUtils.class.getName());
+ return WordUtils.capitalizeFully(str);
+ }
+ public static void main(String[] args) {
+ String message = "sentence to capitalize";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + capitalizeWords(message));
+ }
+
+ private Main() {
+ }
+}
diff --git a/sigil/bld-ivy/example/dependence/depender/build.xml b/sigil/bld-ivy/example/dependence/depender/build.xml
new file mode 100644
index 0000000..f2f9190
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/build.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="depender" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <!-- =================================
+ target: init
+ ================================= -->
+ <target name="init">
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar}"/>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+ </target>
+
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="init"
+ description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}" dot="true"/>
+ </target>
+
+ <!-- =================================
+ target: gen-graph
+ ================================= -->
+ <target name="gen-graph" depends="report" description="--> generates a graph of dependencies (requires dot in your path - see http://www.graphviz.org/)">
+ <property name="dot.file" value="${build.dir}/apache-depending-default.dot" />
+ <property name="ivygraph.output.file" value="${build.dir}/graph.png" />
+ <exec executable="dot">
+ <arg line="-T png -o ${ivygraph.output.file} ${dot.file}" />
+ </exec>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="clean, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="depending.Main"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name="sigil.properties" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/sigil/bld-ivy/example/dependence/depender/ivy.xml b/sigil/bld-ivy/example/dependence/depender/ivy.xml
new file mode 100644
index 0000000..d8031f3
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="depender"/>
+ <dependencies>
+ <dependency name="dependee" rev="latest.integration" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/dependence/depender/sigil.properties b/sigil/bld-ivy/example/dependence/depender/sigil.properties
new file mode 100644
index 0000000..e76ec83
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/sigil.properties
@@ -0,0 +1,7 @@
+# depender sigil.properties
+
+-bundles: depender
+
+-imports: standalone
+
+# end
diff --git a/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java b/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java
new file mode 100644
index 0000000..59b0399
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/depender/src/depending/Main.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package depending;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+ public static void main(String[] args) {
+ String standaloneVersion = standalone.Main.getVersion();
+ if (standaloneVersion != null) {
+ System.out.println("you are using version " + standaloneVersion
+ + " of class " + standalone.Main.class.getName());
+ } else {
+ System.err.println("failed to get version of " + standalone.Main.class.getName());
+ }
+ String message = "i am " + Main.class.getName()
+ + " and " + standalone.Main.class.getName() + " will do the job for me";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + standalone.Main.capitalizeWords(message));
+ }
+
+ private Main() {
+ }
+}
diff --git a/sigil/bld-ivy/example/dependence/settings/ivysettings.properties b/sigil/bld-ivy/example/dependence/settings/ivysettings.properties
new file mode 100644
index 0000000..737733c
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/ivysettings.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+repository.dir=${ivy.settings.dir}/repository
+sigil-ivy-plugin.jar=${ivy.settings.dir}/../../../lib/sigil-ivy-plugin.jar
+ivy.jar=/opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
diff --git a/sigil/bld-ivy/example/dependence/settings/ivysettings.xml b/sigil/bld-ivy/example/dependence/settings/ivysettings.xml
new file mode 100644
index 0000000..5dd4e63
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/ivysettings.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/ivysettings.properties"/>
+ <settings defaultResolver="projects"/>
+ <caches defaultCacheDir="${ivy.settings.dir}/ivy-cache" />
+
+ <classpath file="${sigil-ivy-plugin.jar}" />
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil-resolver" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser/>
+ </parsers>
+
+ <resolvers>
+ <sigil-resolver name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+ <filesystem name="projects">
+ <artifact pattern="${repository.dir}/[artifact]-[revision].[ext]" />
+ <ivy pattern="${repository.dir}/[module]-[revision].xml" />
+ </filesystem>
+ <!--
+ <ibiblio name="libraries" m2compatible="true" usepoms="false" />
+ -->
+ </resolvers>
+ <modules>
+ <module organisation="sigil" name="*" resolver="sigil"/>
+ </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties b/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties
new file mode 100644
index 0000000..85150e2
--- /dev/null
+++ b/sigil/bld-ivy/example/dependence/settings/sigil-repos.properties
@@ -0,0 +1,17 @@
+# repository config
+
+-repositories: system, project, spring
+
+system;provider: system
+system;level: -1
+
+project;provider: project
+project;level: 0
+project;pattern: ../*/[sigilproject]
+
+spring;provider: obr
+spring;level: 2
+spring;url: http://sigil.codecauldron.org/spring-external.obr
+spring;index: ../settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/example/felix-log/README b/sigil/bld-ivy/example/felix-log/README
new file mode 100644
index 0000000..df71d9b
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/README
@@ -0,0 +1,22 @@
+# http://sigil.codecauldron.org
+
+This example shows how to build the felix log service, using Ant/Ivy,
+instead of Maven.
+
+sigil.properties has been constructed to produce an identical bundle to the
+one produced by Maven.
+
+The Felix dependencies are obtained from an OBR index of
+http://repo1.maven.org/maven2/org/apache/felix, Maven repos can't be searched
+directly for package import dependencies.
+
+1. checkout felix log code
+ $ cd felix-log
+ $ svn co http://svn.apache.org/repos/asf/felix/trunk/log
+
+2. set ivy.jar location in common/build.properties
+
+3. build
+ $ cd log
+ $ ant
+
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/build.properties b/sigil/bld-ivy/example/felix-log/bldcommon/build.properties
new file mode 100644
index 0000000..ff1bcc1
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/build.properties
@@ -0,0 +1,26 @@
+# common properties
+
+# CHANGE ivy.jar TO REFLECT YOUR INSTALLATION
+ivy.jar = /opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
+
+sigil-ivy-plugin.jar = ${common.dir}/../../../lib/sigil-ivy-plugin.jar
+
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+deps.dir = ${build.dir}/deps
+resource.dir = ${basedir}/xml
+src.dir = ${basedir}/src
+
+dest.dir = ${build.dir}
+dest.lib.dir = ${dest.dir}/lib
+dest.etc.dir = ${dest.dir}/etc
+
+ivy.file = ${basedir}/ivy.xml
+module.version.target = 1.0
+
+cache.dir = ${common.dir}/ivy-cache
+repository.dir = ${common.dir}/repository
+
+example.version = 1.0.0
+bundle.version = ${example.version}
+
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/common.xml b/sigil/bld-ivy/example/felix-log/bldcommon/common.xml
new file mode 100644
index 0000000..8431484
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/common.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <dirname property="common.dir" file="${ant.file.common}"/>
+ <property file="${common.dir}/build.properties"/>
+
+ <path id="lib.path.id">
+ <fileset dir="${deps.dir}" />
+ </path>
+
+ <path id="sigil.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <!-- setup ivy default configuration with some custom info -->
+ <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+ <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+ <!-- =================================
+ target: load-ivy
+ ================================= -->
+ <target name="load-ivy" depends="ivy-taskdefs">
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ </target>
+
+ <!-- =================================
+ target: ivy-taskdefs
+ ================================= -->
+ <target name="ivy-taskdefs" unless="ivy.loaded">
+ <property name="ivy.loaded" value="true"/>
+ <echo message="Loading Ivy ... common.dir=${common.dir}"/>
+
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar}"/>
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+ </target>
+
+
+
+ <!-- =================================
+ target: build (default target)
+ ================================= -->
+ <target name="build" depends="ident, jar, resources" />
+
+ <target name="ident">
+ <echo message="${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="load-ivy, clean-deps"
+ description="--> resolve and retrieve dependencies with ivy">
+ <mkdir dir="${deps.dir}"/>
+ <ivy:resolve file="${ivy.file}"/>
+ <ivy:retrieve pattern="${deps.dir}/[artifact].[ext]" />
+ </target>
+
+ <!-- =================================
+ target: install
+ ================================= -->
+ <target name="install" depends="install-version, build"
+ description="--> publish this project in the local repository">
+ <ivy:publish artifactspattern="${dest.lib.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${version}"
+ pubdate="${now}"
+ forcedeliver="true"
+ status="integration"/>
+ <echo message="project ${ant.project.name} published locally with version ${version}" />
+ </target>
+
+ <target name="install-version">
+ <tstamp>
+ <format property="now" pattern="yyyyMMddHHmmss"/>
+ </tstamp>
+ <property name="version"
+ value="${module.version.target}-local-${now}"/>
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve"
+ description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve"
+ description="--> compile the project">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}"
+ destdir="${classes.dir}"
+ classpathref="lib.path.id"
+ target="1.5"
+ debug="true" />
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="compile"
+ description="--> make a jar file for this project">
+ <mkdir dir="${dest.lib.dir}"/>
+ <sigil.bundle destpattern="${dest.lib.dir}/[id].[ext]"
+ classpathref="sigil.path.id"/>
+ </target>
+
+ <!-- =================================
+ target: resources
+ ================================= -->
+ <available file="${resource.dir}" type="dir"
+ property="resource.dir.present"/>
+
+ <target name="resources" if="resource.dir.present"
+ description="--> filter xml resources replacing ${VERSION} etc.">
+ <mkdir dir="${dest.etc.dir}"/>
+ <copy todir="${dest.etc.dir}">
+ <fileset dir="${resource.dir}">
+ <include name="*.composite"/>
+ <include name="*.system"/>
+ </fileset>
+ <filterset begintoken="$${" endtoken="}">
+ <filter token="VERSION" value="${bundle.version}"/>
+ <filter token="BLITZ_VERSION" value="${blitz.version}"/>
+ <filter token="JINI_VERSION" value="${jini.version}"/>
+ </filterset>
+ </copy>
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local"
+ description="--> cleans the local repository for the current module">
+ <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-deps
+ ================================= -->
+ <target name="clean-deps"
+ description="--> clean the project libraries directory (dependencies)">
+ <delete includeemptydirs="true" dir="${deps.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-build
+ ================================= -->
+ <target name="clean-build"
+ description="--> clean the project built files">
+ <delete includeemptydirs="true" dir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" depends="clean-build, clean-deps"
+ description="--> clean the project" />
+
+
+ <!-- =================================
+ target: buildlist
+ ================================= -->
+ <fileset id="projects" dir="${basedir}" includes="**/build.xml"/>
+
+ <target name="buildlist" depends="load-ivy">
+ <ivy:buildlist reference="ordered-list"
+ onMissingDescriptor="skip">
+ <fileset refid="projects"/>
+ </ivy:buildlist>
+ </target>
+
+ <!-- =================================
+ target: install-list
+ ================================= -->
+ <target name="install-list" depends="buildlist"
+ description="--> compile, jar and install all projects in the right order">
+ <subant target="install" buildpathref="ordered-list">
+ <propertyset>
+ <propertyref name="ivy.loaded" />
+ </propertyset>
+ </subant>
+ </target>
+
+ <!-- =================================
+ target: clean-list
+ ================================= -->
+ <target name="clean-list" depends="clean, buildlist"
+ description="--> clean all projects">
+ <subant target="clean" buildpathref="ordered-list" />
+ </target>
+
+ <!-- =================================
+ target: clean-all
+ ================================= -->
+ <target name="clean-all" depends="clean-list"
+ description="--> delete repository, ivy cache, and all projects">
+ <delete dir="${repository.dir}"/>
+ <ivy:cleancache />
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml b/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml
new file mode 100644
index 0000000..dd336c7
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/ivysettings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/build.properties"/>
+ <caches defaultCacheDir="${cache.dir}" />
+
+ <settings defaultResolver="local" circularDependencyStrategy="error" />
+
+ <classpath file="${sigil-ivy-plugin.jar}" />
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser config="${ivy.settings.dir}/sigil-repos.properties"
+ quiet="true"/>
+ </parsers>
+
+ <resolvers>
+ <sigil name="sigil"
+ config="${ivy.settings.dir}/sigil-repos.properties"
+ extractBCP="true"/>
+
+ <filesystem name="local">
+ <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+ </filesystem>
+
+ <filesystem name="shared">
+ <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+ </filesystem>
+ </resolvers>
+
+ <modules>
+ <module organisation="sigil" resolver="sigil"/>
+ </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties b/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties
new file mode 100644
index 0000000..fbc95c6
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/bldcommon/sigil-repos.properties
@@ -0,0 +1,17 @@
+# sigil repository config
+
+-repositories: system, project, felix
+
+system;provider: system
+system;level: -1
+
+project;provider: project
+project;level: 0
+project;pattern: ../*/[sigilproject]
+
+felix;provider: obr
+felix;level: 1
+felix;url: http://sigil.codecauldron.org/maven-felix.obr
+felix;index: ../bldcommon/maven-felix.obr
+
+# end
diff --git a/sigil/bld-ivy/example/felix-log/log/build.xml b/sigil/bld-ivy/example/felix-log/log/build.xml
new file mode 100644
index 0000000..c7c19f6
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/build.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="felix-log" default="build">
+ <import file="../bldcommon/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/felix-log/log/ivy.xml b/sigil/bld-ivy/example/felix-log/log/ivy.xml
new file mode 100644
index 0000000..6e0b4db
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.felix"
+ module="felix-log"
+ status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/felix-log/log/sigil.properties b/sigil/bld-ivy/example/felix-log/log/sigil.properties
new file mode 100644
index 0000000..3443218
--- /dev/null
+++ b/sigil/bld-ivy/example/felix-log/log/sigil.properties
@@ -0,0 +1,38 @@
+# sigil project file, saved by plugin.
+
+-activator: ${Bundle-SymbolicName}.impl.Activator
+
+name: org.apache.felix.log
+
+version: 0.9.0.SNAPSHOT
+
+-bundles: \
+ felix-log, \
+
+-contents: \
+ ${Bundle-SymbolicName}.impl, \
+ org.osgi.service.log, \
+
+-resources: \
+ =src/main/resources, \
+
+-sourcedirs: \
+ src/main/java, \
+
+-exports: \
+ org.osgi.service.log, \
+
+-imports: \
+ org.osgi.framework;version=1.4, \
+ org.osgi.service.log;version=1.3, \
+
+header;Bundle-Vendor: The Apache Software Foundation
+
+header;Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+
+header;Export-Service: org.osgi.service.log.LogService,org.osgi.service.log.LogReaderService
+
+header;Bundle-Name: Apache Felix Log Service
+
+header;Bundle-DocURL: http://www.apache.org/
+
diff --git a/sigil/bld-ivy/example/multi-project/README b/sigil/bld-ivy/example/multi-project/README
new file mode 100644
index 0000000..ed39dc3
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/README
@@ -0,0 +1,19 @@
+# http://sigil.codecauldron.org
+
+This example is based on the Ivy Tutorial multi-project example:
+http://ant.apache.org/ivy/history/latest-milestone/tutorial/multiproject.html
+
+Dependencies are resolved using an OBR index for the Spring repository,
+hosted at sigil.codecauldron.org.
+
+projects/sigil-defaults.properties specifies default package version ranges,
+which are inherited by the -imports in sigil.properties.
+
+1. set ivy.jar location in common/build.properties
+
+2. test build order (determined automatically from the dependencies)
+$ ant order
+
+3. build
+$ ant publish
+
diff --git a/sigil/bld-ivy/example/multi-project/build.xml b/sigil/bld-ivy/example/multi-project/build.xml
new file mode 100644
index 0000000..a04ef74
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/build.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="all" default="publish" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <property name="common.dir" value="${basedir}/common" />
+ <import file="${common.dir}/common.xml"/>
+
+ <fileset id="projects" dir="projects" includes="**/build.xml"/>
+
+ <path id="unordered-list">
+ <fileset refid="projects"/>
+ </path>
+
+ <target name="buildlist" depends="load-ivy">
+ <ivy:buildlist reference="ordered-list">
+ <fileset refid="projects"/>
+ </ivy:buildlist>
+ </target>
+
+ <target name="publish" depends="buildlist"
+ description="compile, jar and publish all projects in the right order">
+ <subant target="publish" buildpathref="ordered-list">
+ <propertyset>
+ <propertyref name="ivy.loaded" />
+ </propertyset>
+ </subant>
+ </target>
+
+ <target name="clean" description="clean all projects">
+ <subant target="clean" buildpathref="unordered-list" />
+ </target>
+
+ <target name="fullclean" depends="clean, load-ivy"
+ description="clean tutorial: delete repository, ivy cache, and all projects">
+ <delete dir="repository"/>
+ <ivy:cleancache />
+ </target>
+
+ <target name="order" depends="buildlist"
+ description="test buildlist order">
+ <subant target="hello" buildpathref="ordered-list" />
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/common/build.properties b/sigil/bld-ivy/example/multi-project/common/build.properties
new file mode 100644
index 0000000..79298b4
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/build.properties
@@ -0,0 +1,36 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+
+# CHANGE ivy.jar TO REFLECT YOUR INSTALLATION
+ivy.jar = /opt/apache-ivy-2.0.0-rc2/ivy-2.0.0-rc2.jar
+
+sigil-ivy-plugin.jar = ${common.dir}/../../../lib/sigil-ivy-plugin.jar
+
+lib.dir = ${basedir}/lib
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+src.dir = ${basedir}/src
+repository.dir=${common.dir}/../repository
+
+ivy.file = ${basedir}/ivy.xml
+
+jar.file = ${build.dir}/${ant.project.name}.jar
+main.class.name = ${ant.project.name}.Main
+
+module.version.target = 1.0
diff --git a/sigil/bld-ivy/example/multi-project/common/common.xml b/sigil/bld-ivy/example/multi-project/common/common.xml
new file mode 100644
index 0000000..f464e32
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/common.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- a sample common ant build file, used for ivy multi-project tutorial
+ feel free to copy and adapt it to your own needs
+ Note that the only targets specific to ivy are:
+ load-ivy
+ resolve
+ report
+ ivy-new-version
+ publish
+ publish-local
+
+ All other targets are usual ant based targets, which could have been written
+ in a build not depending at all on ivy:
+ resolve constructs a lib directory based upon ivy dependencies, and then the lib dir
+ is used as in any classical ant build
+ -->
+
+ <property file="${common.dir}/build.properties"/>
+
+ <!-- =================================
+ target: load-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy 2.0 in your ant lib, you can simply remove this
+ target
+ ================================= -->
+
+ <target name="load-ivy" depends="ivy-taskdefs">
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ </target>
+
+ <target name="ivy-taskdefs" unless="ivy.loaded">
+ <property name="ivy.loaded" value="true"/>
+ <echo message="Loading Ivy ..."/>
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar}"/>
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+ </target>
+
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+
+ <!-- setup ivy default configuration with some custom info -->
+ <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+ <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+ <!-- here is how we would have configured ivy if we had our own ivysettings file
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ -->
+
+ <!-- =================================
+ target: hello
+ ================================= -->
+ <target name="hello" description="--> identify project (useful to test buildlist)">
+ <echo message="${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="clean-lib, load-ivy" description="--> resolve and retrieve dependencies with ivy">
+ <mkdir dir="${lib.dir}"/> <!-- not usually necessary, ivy creates the directory IF there are dependencies -->
+
+ <!-- the call to resolve is not mandatory, retrieve makes an implicit call if we don't -->
+ <ivy:resolve file="${ivy.file}"/>
+ <ivy:retrieve pattern="${lib.dir}/[artifact].[ext]" />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> compile the project">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" debug="true" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="version, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="${main.class.name}"/>
+ </target>
+
+ <target name="ivy-new-version" depends="load-ivy" unless="ivy.new.revision">
+ <!-- default module version prefix value -->
+ <property name="module.version.prefix" value="${module.version.target}-dev-b" />
+
+ <!-- asks to ivy an available version number -->
+ <ivy:info file="${ivy.file}" />
+ <ivy:buildnumber
+ organisation="${ivy.organisation}" module="${ivy.module}"
+ revision="${module.version.prefix}" defaultBuildNumber="1" revSep=""/>
+ </target>
+
+ <target name="local-version">
+ <tstamp>
+ <format property="now" pattern="yyyyMMddHHmmss"/>
+ </tstamp>
+ <property name="ivy.new.revision" value="${module.version.target}-local-${now}"/>
+ </target>
+
+ <target name="version" depends="ivy-new-version">
+ <!-- create version file in classpath for later inclusion in jar -->
+ <mkdir dir="${classes.dir}"/>
+ <echo message="version=${ivy.new.revision}" file="${classes.dir}/${ant.project.name}.properties" append="false" />
+
+ <!-- load generated version properties file -->
+ <property file="${classes.dir}/${ant.project.name}.properties" />
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="version, compile" description="--> make a jar file for this project">
+
+ <!--
+ <jar destfile="${jar.file}">
+ <fileset dir="${classes.dir}" />
+ <manifest>
+ <attribute name="Built-By" value="${user.name}"/>
+ <attribute name="Build-Version" value="${version}" />
+ </manifest>
+ </jar>
+ -->
+
+ <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+ classpathref="run.path.id"/>
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="clean-build, jar" description="--> publish this project in the ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="shared"
+ pubrevision="${version}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: publish-local
+ ================================= -->
+ <target name="publish-local" depends="local-version, jar" description="--> publish this project in the local ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${version}"
+ pubdate="${now}"
+ status="integration"
+ forcedeliver="true"
+ />
+ <echo message="project ${ant.project.name} published locally with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local" description="--> cleans the local repository for the current module">
+ <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-lib
+ ================================= -->
+ <target name="clean-lib" description="--> clean the project libraries directory (dependencies)">
+ <delete includeemptydirs="true" dir="${lib.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-build
+ ================================= -->
+ <target name="clean-build" description="--> clean the project built files">
+ <delete includeemptydirs="true" dir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" depends="clean-build, clean-lib" description="--> clean the project" />
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/common/ivysettings.xml b/sigil/bld-ivy/example/multi-project/common/ivysettings.xml
new file mode 100644
index 0000000..614aa0e
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/ivysettings.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/build.properties"/>
+ <caches defaultCacheDir="${ivy.settings.dir}/../ivy-cache" />
+
+ <settings defaultResolver="shared" circularDependencyStrategy="error" />
+
+ <classpath file="${ivy.settings.dir}/../../../lib/sigil-ivy-plugin.jar" />
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil-resolver" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser quiet="true"/>
+ </parsers>
+
+ <resolvers>
+ <sigil-resolver name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+ <filesystem name="local">
+ <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+ </filesystem>
+ <filesystem name="shared">
+ <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+ </filesystem>
+ </resolvers>
+ <modules>
+ <!--<module organisation="org.apache.ivy.example" name=".*" resolver="shared"/>
+ -->
+ <module organisation="sigil" resolver="sigil"/>
+ </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties b/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties
new file mode 100644
index 0000000..bd711fa
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/common/sigil-repos.properties
@@ -0,0 +1,20 @@
+
+# repository config
+
+-repositories: system, project, spring
+
+system;provider: system
+system;level: -1
+#system;framework: osgi.core.jar
+#system;profile: J2SE-1.5
+
+project;provider: project
+project;level: 0
+project;pattern: ../projects/**/[sigilproject]
+
+spring;provider: obr
+spring;level: 2
+spring;url: http://sigil.codecauldron.org/spring-external.obr
+spring;index: ../common/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/build.properties b/sigil/bld-ivy/example/multi-project/projects/console/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/build.xml b/sigil/bld-ivy/example/multi-project/projects/console/build.xml
new file mode 100644
index 0000000..5c02cba
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="console" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml
new file mode 100644
index 0000000..b1eb4f4
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/ivy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="console"
+ status="integration"/>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->standalone" />
+ <dependency name="find" rev="latest.integration" conf="default->standalone" />
+ <dependency name="sizewhere" rev="latest.integration" conf="default->standalone" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties
new file mode 100644
index 0000000..dea089f
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/sigil.properties
@@ -0,0 +1,6 @@
+
+-bundles: console
+
+-exports: console
+
+-imports: list
diff --git a/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java b/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java
new file mode 100644
index 0000000..678f3ca
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/console/src/console/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package console;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Arrays;
+import java.lang.reflect.Method;
+
+
+public class Main {
+ private static Collection QUIT_COMMANDS = Arrays.asList(new String[] {"quit", "q", "exit"});
+ private static Collection HELP_COMMANDS = Arrays.asList(new String[] {"help", "h", "?"});
+
+ public static void main(String[] a) throws Exception {
+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+ while (true) {
+ System.out.print("> ");
+ String command = in.readLine().trim();
+ if (QUIT_COMMANDS.contains(command)) {
+ return;
+ }
+ if (HELP_COMMANDS.contains(command)) {
+ help();
+ continue;
+ }
+ String[] split = command.split(" ");
+ if (split.length == 0) {
+ error(command);
+ continue;
+ }
+
+ try {
+ String[] args = new String[split.length - 1];
+ System.arraycopy(split, 1, args, 0, args.length);
+ Class cl = Class.forName(split[0]+".Main");
+ Method m = cl.getMethod("main", new Class[] {String[].class});
+ m.invoke(null, new Object[] {args});
+ } catch (Exception ex) {
+ error(command);
+ continue;
+ }
+ }
+ }
+
+ private static void help() {
+ System.out.println("available commands:");
+ System.out.println("\tquit: quit the console");
+ System.out.println("\thelp: displays this message");
+ System.out.println("\tlist -dir <dir>: list files in given directory");
+ System.out.println("\tfind -dir <dir> -name <name>: list files with given name in given dir");
+ System.out.println("\tsizewhere -dir <dir> -name <name>: compute total size of files with given name in given dir");
+ System.out.println("\thelp: displays this message");
+ }
+
+ private static void error(String command) {
+ System.out.println("unknown command "+command);
+ System.out.println("type ? for help");
+ }
+
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/build.properties b/sigil/bld-ivy/example/multi-project/projects/find/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/build.xml b/sigil/bld-ivy/example/multi-project/projects/find/build.xml
new file mode 100644
index 0000000..4d23bf5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="find" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml
new file mode 100644
index 0000000..b7f1bc1
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="find"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="find" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="list" rev="latest.integration" conf="core" />
+ <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties
new file mode 100644
index 0000000..30c10a7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: find
+
+-exports: find
+
+-imports:\
+ version, \
+ list, \
+ org.apache.commons.collections, \
+ org.apache.commons.cli
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java b/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java
new file mode 100644
index 0000000..25fa1af
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/src/find/FindFile.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import version.Version;
+import list.ListFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Predicate;
+
+public class FindFile {
+ static {
+ Version.register("find");
+ }
+
+ public static Collection find(File dir, String name) {
+ return find(ListFile.list(dir), name);
+ }
+
+ private static Collection find(Collection files, final String name) {
+ return CollectionUtils.select(files, new Predicate() {
+ public boolean evaluate(Object o) {
+ return ((File)o).getName().indexOf(name) != -1;
+ }
+ });
+ }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java b/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java
new file mode 100644
index 0000000..a41c136
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/find/src/find/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName("dir")
+ .hasArg()
+ .withDescription("list files in given dir")
+ .create("dir");
+ Option name = OptionBuilder.withArgName("name")
+ .hasArg()
+ .withDescription("list files with given name")
+ .create("name");
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse(options, args);
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ Collection files = FindFile.find(dir, name);
+ System.out.println("listing files in " + dir + " containing " + name);
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ System.out.println("\t" + it.next() + "\n");
+ }
+ } catch(ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("find", options);
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/build.properties b/sigil/bld-ivy/example/multi-project/projects/list/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/build.xml b/sigil/bld-ivy/example/multi-project/projects/list/build.xml
new file mode 100644
index 0000000..3926148
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="list" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml
new file mode 100644
index 0000000..6593fb7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/ivy.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="list"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="list" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties
new file mode 100644
index 0000000..302cba7
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/sigil.properties
@@ -0,0 +1,14 @@
+
+# requirements
+
+-imports:\
+ version, \
+ org.apache.commons.cli
+
+# exports
+
+-bundles: list
+
+list;name: org.example.list
+list;-exports: list.*
+
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java b/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java
new file mode 100644
index 0000000..7467152
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/src/list/ListFile.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import version.Version;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class ListFile {
+ static {
+ Version.register("list");
+ }
+
+ public static Collection list(File dir) {
+ Collection files = new ArrayList();
+
+ return list(dir, files);
+ }
+
+ private static Collection list(File file, Collection files) {
+ if (file.isDirectory()) {
+ File[] f = file.listFiles();
+ for (int i=0; i<f.length; i++) {
+ list(f[i], files);
+ }
+ } else {
+ files.add(file);
+ }
+ return files;
+ }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java b/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java
new file mode 100644
index 0000000..0f8cb14
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/list/src/list/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName( "dir" )
+ .hasArg()
+ .withDescription( "list files in given dir" )
+ .create( "dir" );
+ Options options = new Options();
+
+ options.addOption(dir);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse( options, args );
+ File dir = new File(line.getOptionValue("dir", "."));
+ Collection files = ListFile.list(dir);
+ System.out.println("listing files in "+dir);
+ for (Iterator it = files.iterator(); it.hasNext(); ) {
+ System.out.println("\t"+it.next()+"\n");
+ }
+ } catch( ParseException exp ) {
+ // oops, something went wrong
+ System.err.println( "Parsing failed. Reason: " + exp.getMessage() );
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp( "list", options );
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties b/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties
new file mode 100644
index 0000000..d61df45
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sigil-defaults.properties
@@ -0,0 +1,4 @@
+# sigil-defaults.properties
+
+package;org.apache.commons.cli: [1.1,1.2)
+package;org.apache.commons.collections: [3.0,4.0)
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/build.properties b/sigil/bld-ivy/example/multi-project/projects/size/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/build.xml b/sigil/bld-ivy/example/multi-project/projects/size/build.xml
new file mode 100644
index 0000000..2a75fe5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="size" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml
new file mode 100644
index 0000000..7112b7f
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/ivy.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="size"
+ status="integration"/>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->core" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties
new file mode 100644
index 0000000..cf239af
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/sigil.properties
@@ -0,0 +1,8 @@
+
+-bundles: size
+
+-exports: size
+
+-imports:\
+ list, \
+ version
diff --git a/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java b/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java
new file mode 100644
index 0000000..dcd4a1c
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/size/src/size/FileSize.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package size;
+
+import version.Version;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+public class FileSize {
+ static {
+ Version.register("size");
+ }
+
+ public static long totalSize(File dir) {
+ return totalSize(list.ListFile.list(dir));
+ }
+
+ public static long totalSize(Collection files) {
+ long total = 0;
+ for (Iterator it = files.iterator(); it.hasNext(); ) {
+ File f = (File)it.next();
+ total += f.length();
+ }
+ return total;
+ }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml
new file mode 100644
index 0000000..0b8ab4c
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="sizewhere" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml
new file mode 100644
index 0000000..d50bddb
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="sizewhere"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="sizewhere" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="size" rev="latest.integration" conf="core->default" />
+ <dependency name="find" rev="latest.integration" conf="core" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties
new file mode 100644
index 0000000..c272f9b
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: sizewhere
+
+-exports: sizewhere
+
+-imports:\
+ find, \
+ size, \
+ version, \
+ org.apache.commons.cli
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
new file mode 100644
index 0000000..131af66
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName( "dir" )
+ .hasArg()
+ .withDescription( "give total size of files in given dir" )
+ .create( "dir" );
+ Option name = OptionBuilder.withArgName( "name" )
+ .hasArg()
+ .withDescription( "give total size of files with given name" )
+ .create( "name" );
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse( options, args );
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ System.out.println("total size of files in "+dir+" containing "+name+": "+SizeWhere.totalSize(dir, name));
+ } catch( ParseException exp ) {
+ // oops, something went wrong
+ System.err.println( "Parsing failed. Reason: " + exp.getMessage() );
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp( "sizewhere", options );
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
new file mode 100644
index 0000000..3594bd9
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import version.Version;
+import size.FileSize;
+import find.FindFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class SizeWhere {
+ static {
+ Version.register("sizewhere");
+ }
+
+ public static long totalSize(File dir, String name) {
+ return FileSize.totalSize(FindFile.find(dir, name));
+ }
+}
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/build.properties b/sigil/bld-ivy/example/multi-project/projects/version/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/build.xml b/sigil/bld-ivy/example/multi-project/projects/version/build.xml
new file mode 100644
index 0000000..375db20
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="version" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml b/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml
new file mode 100644
index 0000000..16e08f3
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="version"
+ status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties b/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties
new file mode 100644
index 0000000..a940ffd
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/sigil.properties
@@ -0,0 +1,4 @@
+
+-bundles: version
+
+-exports: version
diff --git a/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java b/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java
new file mode 100644
index 0000000..0c69bc5
--- /dev/null
+++ b/sigil/bld-ivy/example/multi-project/projects/version/src/version/Version.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package version;
+
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+public class Version {
+ static {
+ versions = new HashMap();
+ register("version");
+ }
+
+ private static Map versions;
+
+ public static void register(String module) {
+ try {
+ InputStream moduleVersion = Version.class.getResourceAsStream("/"+module+".properties");
+ Properties props = new Properties();
+ props.load(moduleVersion);
+ String version = (String)props.get("version");
+ versions.put(module, version);
+ System.out.println("--- using "+module+" v"+version);
+ } catch (Exception ex) {
+ System.err.println("an error occured while registering "+module+": "+ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/sigil/bld-ivy/sigil-ivy-plugin.bnd b/sigil/bld-ivy/sigil-ivy-plugin.bnd
new file mode 100644
index 0000000..7fb4156
--- /dev/null
+++ b/sigil/bld-ivy/sigil-ivy-plugin.bnd
@@ -0,0 +1,23 @@
+# sigil-ivy-plugin.jar
+
+Bundle-DocURL: http://sigil.codecauldron.org
+Bundle-Vendor: Paremus Limited
+Bundle-Version: ${version}
+
+# we depend on bndlib, but don't want to embed it,
+# in case other tasks want a different version.
+# So just add Class-Path to the manifest, so we load bndlib.jar
+# if it's in the same directory as the sigil-ivy-plugin.
+Class-Path: bndlib.jar
+
+# embed other dependencies directly in the plugin
+Private-Package: \
+ org.cauldron.*, \
+ profiles.*, \
+ org.eclipse.core.runtime, \
+ org.osgi.framework
+
+-removeheaders: TODAY, DSTAMP, TSTAMP
+-pedantic: true
+
+# end
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java
new file mode 100644
index 0000000..572b64f
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleInfoTask.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+public class BundleInfoTask extends Task {
+ private File bundle;
+ private String header;
+ private String property;
+ private String defaultValue;
+
+ @Override
+ public void execute() throws BuildException {
+ if (bundle == null)
+ throw new BuildException("missing attribute: bundle");
+ if (header == null)
+ throw new BuildException("missing attribute: header");
+
+ try {
+ JarFile jar = new JarFile(bundle);
+ Manifest mf = jar.getManifest();
+ String value = mf.getMainAttributes().getValue(header);
+ if ( property == null ) {
+ log(header + "=" + value);
+ }
+ else {
+ if ("Bundle-SymbolicName".equals(header) && value != null) {
+ // remove singleton flag
+ int semi = value.indexOf(';');
+ if (semi > 0)
+ value = value.substring(0, semi);
+ }
+ if ( value == null ) {
+ value = defaultValue;
+ }
+ if ( value != null ) {
+ getProject().setNewProperty(property, value);
+ }
+ }
+ } catch (IOException e) {
+ throw new BuildException( "Failed to access bundle", e);
+ }
+ }
+
+ public void setBundle(String bundle) {
+ this.bundle = new File( bundle );
+ }
+
+ public void setHeader(String header) {
+ this.header = header;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java
new file mode 100644
index 0000000..f8ab762
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ant/BundleTask.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+
+public class BundleTask extends Task {
+ private File[] classpath;
+ private String destPattern;
+ private boolean force;
+ private String property;
+ private String sigilFile;
+
+ @Override
+ public void execute() throws BuildException {
+ if (classpath == null)
+ throw new BuildException("missing: attribute: classpathref");
+ if (destPattern == null)
+ throw new BuildException("missing attribute: destpattern");
+
+ IBldProject project;
+
+ try {
+ project = BldFactory.getProject(getSigilFileURI());
+
+ } catch (IOException e) {
+ throw new BuildException("failed to get project file: " + e);
+ }
+
+ Properties env = new Properties();
+ @SuppressWarnings("unchecked") Hashtable<String, String> properties = getProject().getProperties();
+ for (String key : properties.keySet()) {
+ if (key.matches("^[a-z].*")) { // avoid props starting with Uppercase - bnd adds them to manifest
+ env.setProperty(key, properties.get(key));
+ }
+ }
+
+ BundleBuilder bb = new BundleBuilder(project, classpath, destPattern, env);
+ boolean anyModified = false;
+
+ for (IBldBundle bundle : project.getBundles()) {
+ String id = bundle.getId();
+ log("creating bundle: " + id);
+ int nWarn = 0;
+ int nErr = 0;
+ String msg = "";
+
+ try {
+ boolean modified = (bb.createBundle(bundle, force, new BundleBuilder.Log() {
+ public void warn(String msg) {
+ log(msg, Project.MSG_WARN);
+ }
+ public void verbose(String msg) {
+ log(msg, Project.MSG_VERBOSE);
+ }
+ }));
+ nWarn = bb.warnings().size();
+ if (modified) {
+ anyModified = true;
+ } else {
+ msg = " (not modified)";
+ }
+ } catch (Exception e) {
+ List<String> errors = bb.errors();
+ if (errors != null) {
+ nErr = errors.size();
+ for (String err : errors) {
+ log(err, Project.MSG_ERR);
+ }
+ }
+ throw new BuildException("failed to create: " + id + ": " + e, e);
+ } finally {
+ log(id + ": " + count(nErr, "error") + ", " + count(nWarn, "warning") + msg);
+ }
+ }
+
+ if (anyModified && property != null) {
+ getProject().setProperty(property, "true");
+ }
+ }
+
+ private URI getSigilFileURI() {
+ File file = sigilFile == null ? new File(getProject().getBaseDir(), IBldProject.PROJECT_FILE) : new File(sigilFile);
+ if ( !file.isFile() ) {
+ throw new BuildException( "File not found " + file.getAbsolutePath() );
+ }
+ return file.toURI();
+ }
+
+ private String count(int count, String msg) {
+ return count + " " + msg + (count == 1 ? "" : "s");
+ }
+
+ public void setDestpattern(String pattern) {
+ this.destPattern = pattern;
+ }
+
+ public void setForce(String force) {
+ this.force = Boolean.parseBoolean(force);
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public void setClasspathref(String value) {
+ Path p = (Path) getProject().getReference(value);
+ if (p == null) {
+ throw new BuildException(value + "is not a path reference.");
+ }
+
+ String[] paths = p.list();
+ classpath = new File[paths.length];
+ for (int i = 0; i < paths.length; ++i) {
+ classpath[i] = new File(paths[i]);
+ }
+ }
+
+ public void setSigilFile(String sigilFile) {
+ this.sigilFile = sigilFile;
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java
new file mode 100644
index 0000000..ce5d9db
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldRepositoryManager.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.config.IRepositoryConfig;
+import org.cauldron.sigil.repository.AbstractRepositoryManager;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+
+public class BldRepositoryManager extends AbstractRepositoryManager {
+ private static Map<String, String> aliases = new HashMap<String, String>();
+
+ static {
+ aliases.put("filesystem", "org.cauldron.bld.core.repository.FileSystemRepositoryProvider");
+ aliases.put("obr", "org.cauldron.bld.obr.OBRRepositoryProvider");
+ aliases.put("project", "org.cauldron.bld.ivy.ProjectRepositoryProvider");
+ aliases.put("system", "org.cauldron.bld.core.repository.SystemRepositoryProvider");
+ };
+
+ private Map<String, Properties> repos;
+
+ public BldRepositoryManager(Map<String, Properties> repos) {
+ this.repos = repos;
+ }
+
+ @Override
+ protected void loadRepositories() {
+ for (String name : repos.keySet()) {
+ Properties repo = repos.get(name);
+
+ String alias = repo.getProperty(IRepositoryConfig.REPOSITORY_PROVIDER);
+ if (alias == null) {
+ Log.error("provider not specified for repository: " + name);
+ continue;
+ }
+
+ String provider = (aliases.containsKey(alias) ? aliases.get(alias) : alias);
+
+ if (alias.equals("obr")) {
+ // cache is directory where synchronized bundles are stored;
+ // not needed in ivy.
+ repo.setProperty("cache", "/no-cache");
+
+ if (!repo.containsKey("index")) {
+ // index is created as copy of url
+ File indexFile = new File(System.getProperty("java.io.tmpdir"), "obr-index-" + name);
+ indexFile.deleteOnExit();
+ repo.setProperty("index", indexFile.getAbsolutePath());
+ }
+ }
+
+ int level = Integer.parseInt(repo.getProperty(IRepositoryConfig.REPOSITORY_LEVEL,
+ IBundleRepository.NORMAL_PRIORITY + ""));
+
+ try {
+ IRepositoryProvider instance = (IRepositoryProvider) (Class.forName(provider).newInstance());
+ IBundleRepository repository = instance.createRepository(name, repo);
+ addRepository(repository, level);
+ Log.verbose("added repository: " + repository);
+ } catch (Exception e) {
+ throw new Error("createRepository() failed: " + repo + " : " + e, e);
+ }
+ }
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java
new file mode 100644
index 0000000..f29f656
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/BldResolver.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+
+public class BldResolver implements IBldResolver {
+ private Map<String, Properties> repos;
+ private BldRepositoryManager manager;
+
+ static {
+ try {
+ BldCore.init();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ };
+
+ public BldResolver(Map<String,Properties> repos) {
+ this.repos = repos;
+ }
+
+ public IResolution resolve(IModelElement element, boolean transitive) {
+ int options = ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INCLUDE_OPTIONAL;
+ if (transitive) options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+
+ ResolutionConfig config = new ResolutionConfig(options);
+ try {
+ return resolve(element, config);
+ } catch (ResolutionException e) {
+ throw new IllegalStateException("eek! this shouldn't happen when ignoreErrors=true", e);
+ }
+ }
+
+ public IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException {
+ int options = 0;
+ if ( transitive ) options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+ ResolutionConfig config = new ResolutionConfig(options);
+ return resolve(element, config);
+ }
+
+ private IResolution resolve(IModelElement element, ResolutionConfig config) throws ResolutionException {
+ if (manager == null) {
+ manager = new BldRepositoryManager(repos);
+ }
+
+ IResolutionMonitor nullMonitor = new IResolutionMonitor() {
+ public void endResolution(IModelElement requirement, ISigilBundle sigilBundle) {
+ }
+
+ public boolean isCanceled() {
+ return false;
+ }
+
+ public void startResolution(IModelElement requirement) {
+ }
+ };
+
+ return manager.getBundleResolver().resolve(element, config, nullMonitor);
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java
new file mode 100644
index 0000000..e914172
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/FindUtil.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class FindUtil {
+ static final String WILD_ANY = "[^.].*";
+ static final String WILD_ONE = "[^.][^;]*"; // WILD_ONE.endsWith(WILD_ANY) == false
+
+ // example pattern: ${repository}/projects/abc*/*/project.sigil
+ public static Collection<File> findFiles(String pattern) throws IOException {
+ int star = pattern.indexOf("*");
+ if (star == -1) {
+ throw new IOException("pattern doesn't contain '*': " + pattern);
+ }
+ int slash = pattern.lastIndexOf('/', star);
+
+ String regex = pattern.substring(slash + 1);
+ regex = regex.replaceAll("\\*\\*", "-wildany-");
+ regex = regex.replaceAll("\\*", "-wildone-");
+ regex = regex.replaceAll("-wildany-", WILD_ANY);
+ regex = regex.replaceAll("-wildone-", WILD_ONE);
+
+ String[] patterns = regex.split("/");
+
+ ArrayList<File> list = new ArrayList<File>();
+ File root = new File(pattern.substring(0, slash));
+
+ if (root.isDirectory()) {
+ findFiles(root, 0, patterns, list);
+ } else {
+ throw new IOException("pattern root directory does not exist: " + root);
+ }
+
+ return list;
+ }
+
+ private static void findFiles(File dir, int level, String[] patterns, Collection<File> list) {
+ final String filePattern = patterns[patterns.length-1];
+ final String dirPattern;
+
+ if (level < patterns.length-1) {
+ dirPattern = patterns[level];
+ } else {
+ dirPattern = "/"; // impossible to match marker
+ }
+
+ final boolean stillWild;
+ if ((level > 0) && (level < patterns.length) && patterns[level-1].endsWith(WILD_ANY)) {
+ stillWild = true;
+ } else {
+ stillWild = false;
+ }
+
+ for (File path : dir.listFiles(new FileFilter() {
+ public boolean accept(File pathname) {
+ String name = pathname.getName();
+ if (pathname.isDirectory()) {
+ return name.matches(dirPattern) || (stillWild && name.matches(WILD_ANY));
+ } else if (dirPattern.equals("/") || dirPattern.equals(WILD_ANY)) {
+ return name.matches(filePattern);
+ } else {
+ return false;
+ }
+ }
+ })) {
+ if (path.isDirectory()) {
+ int inc = path.getName().matches(dirPattern) ? 1 : 0;
+ findFiles(path, level + inc, patterns, list);
+ } else {
+ list.add(path);
+ }
+ }
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java
new file mode 100644
index 0000000..35ff995
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/IBldResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionException;
+
+public interface IBldResolver {
+ IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException;
+ IResolution resolve(IModelElement element, boolean transitive);
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java
new file mode 100644
index 0000000..6724063
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/Log.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import org.apache.ivy.util.Message;
+
+// ensure common prefix to all sigil messages
+public class Log {
+ public static final String PREFIX = "Sigil: ";
+
+ private static final boolean highlight = false;
+
+ public static void error(String msg) {
+ if (highlight)
+ Message.deprecated(PREFIX + "[error] " + msg);
+ Message.error(PREFIX + msg);
+ }
+
+ public static void warn(String msg) {
+ if (highlight)
+ Message.deprecated(PREFIX + "[warn] " + msg);
+ else
+ Message.warn(PREFIX + msg);
+ }
+
+ public static void info(String msg) {
+ if (highlight)
+ Message.deprecated(PREFIX + "[info] " + msg);
+ else
+ Message.info(PREFIX + msg);
+ }
+
+ public static void verbose(String msg) {
+ Message.verbose(PREFIX + "[verbose] " + msg);
+ }
+
+ public static void debug(String msg) {
+ if (highlight)
+ Message.deprecated(PREFIX + "[debug] " + msg);
+ else
+ Message.debug(PREFIX + msg);
+ }
+
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java
new file mode 100644
index 0000000..8dbca34
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepository.java
@@ -0,0 +1,292 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+import org.osgi.framework.Version;
+
+public class ProjectRepository extends AbstractBundleRepository {
+ private ArrayList<ISigilBundle> bundles;
+ private ArrayList<ISigilBundle> wildBundles;
+ private String projectFilePattern;
+
+ /* package */ProjectRepository(String id, String projectFilePattern) {
+ super(id);
+ this.projectFilePattern = projectFilePattern.replaceAll("\\[sigilproject\\]",
+ IBldProject.PROJECT_FILE);
+ }
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ for (ISigilBundle b : getBundles()) {
+ if (!visitor.visit(b)) {
+ break;
+ }
+ }
+ }
+
+ // override to provide fuzzy matching for wild-card exports.
+ @Override
+ public Collection<ISigilBundle> findAllProviders(final IPackageImport pi, int options) {
+ return findProviders(pi, options, false);
+ }
+
+ @Override
+ public ISigilBundle findProvider(IPackageImport pi, int options) {
+ Collection<ISigilBundle> found = findProviders(pi, options, true);
+ return found.isEmpty() ? null : found.iterator().next();
+ }
+
+ private Collection<ISigilBundle> findProviders(final IPackageImport pi, int options,
+ boolean findFirst) {
+ ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+ ILicensePolicy policy = findPolicy(pi);
+ String name = pi.getPackageName();
+ VersionRange versions = pi.getVersions();
+
+ // find exact match(es)
+ for (ISigilBundle bundle : getBundles()) {
+ if (policy.accept(bundle)) {
+ for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+ if (name.equals(exp.getPackageName())
+ && versions.contains(exp.getVersion())) {
+ found.add(bundle);
+ if (findFirst)
+ return found;
+ }
+ }
+ }
+ }
+
+ if (!found.isEmpty())
+ return found;
+
+ // find best fuzzy match
+ ISigilBundle fuzzyMatch = null;
+ int fuzzyLen = 0;
+
+ for (ISigilBundle bundle : getWildBundles()) {
+ if (policy.accept(bundle)) {
+ for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+ String export = exp.getPackageName();
+ if (export.endsWith("*")) {
+ String export1 = export.substring(0, export.length() - 1);
+ if ((name.startsWith(export1) || export1.equals(name + "."))
+ && versions.contains(exp.getVersion())) {
+ if (export1.length() > fuzzyLen) {
+ fuzzyLen = export1.length();
+ fuzzyMatch = bundle;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (fuzzyMatch != null)
+ found.add(fuzzyMatch);
+
+ return found;
+ }
+
+ private synchronized void init() {
+ System.out.println("Sigil: loading Project Repository: " + projectFilePattern);
+
+ ArrayList<File> projects = new ArrayList<File>();
+
+ for (String pattern : projectFilePattern.split("\\s+")) {
+ try {
+ Collection<File> files = FindUtil.findFiles(pattern);
+ if (files.isEmpty()) {
+ Log.warn("ProjectRepository: no projects match: " + pattern);
+ } else {
+ projects.addAll(files);
+ }
+ } catch (IOException e) {
+ // pattern root dir does not exist
+ Log.error("ProjectRepository: " + pattern + ": " + e.getMessage());
+ }
+ }
+
+ if (projects.isEmpty()) {
+ throw new IllegalArgumentException(
+ "ProjectRepository: no projects found using pattern: "
+ + projectFilePattern);
+ }
+
+ bundles = new ArrayList<ISigilBundle>();
+
+ for (File proj : projects) {
+ try {
+ addBundles(proj, bundles);
+ } catch (IOException e) {
+ Log.warn("Skipping project: " + proj + ": " + e.getMessage());
+ } catch (ParseException e) {
+ Log.warn("Skipping project: " + proj + ": " + e.getMessage());
+ }
+ }
+ }
+
+ private List<ISigilBundle> getBundles() {
+ if (bundles == null) {
+ init();
+ }
+ return bundles;
+ }
+
+ private List<ISigilBundle> getWildBundles() {
+ if (wildBundles == null) {
+ wildBundles = new ArrayList<ISigilBundle>();
+ for (ISigilBundle bundle : getBundles()) {
+ for (IPackageExport exp : bundle.getBundleInfo().getExports()) {
+ String export = exp.getPackageName();
+ if (export.endsWith("*")) {
+ wildBundles.add(bundle);
+ break;
+ }
+ }
+ }
+ }
+ return wildBundles;
+ }
+
+ public void refresh() {
+ bundles = null;
+ wildBundles = null;
+ notifyChange();
+ }
+
+ private void addBundles(File file, List<ISigilBundle> list) throws IOException,
+ ParseException {
+ URI uri = file.getCanonicalFile().toURI();
+ IBldProject project = BldFactory.getProject(uri);
+
+ for (IBldBundle bb : project.getBundles()) {
+ IBundleModelElement info = new BundleModelElement();
+
+ for (IPackageExport pexport : bb.getExports()) {
+ info.addExport(pexport);
+ }
+
+ for (IPackageImport import1 : bb.getImports()) {
+ IPackageImport clone = (IPackageImport) import1.clone();
+ clone.setParent(null);
+ info.addImport(clone);
+ }
+
+ for (IRequiredBundle require : bb.getRequires()) {
+ IRequiredBundle clone = (IRequiredBundle) require.clone();
+ clone.setParent(null);
+ info.addRequiredBundle(clone);
+ }
+
+ info.setSymbolicName(bb.getSymbolicName());
+
+ Version version = new Version(bb.getVersion());
+ info.setVersion(version);
+
+ ProjectBundle pb = new ProjectBundle();
+ pb.setBundleInfo(info);
+ pb.setId(bb.getId());
+
+ ModuleDescriptor md = SigilParser.instance().parseDescriptor(uri.toURL());
+
+ ModuleRevisionId mrid = md.getModuleRevisionId();
+ pb.setModule(mrid.getName());
+ pb.setOrg(mrid.getOrganisation());
+ // XXX: should revision be configurable?
+ pb.setRevision("latest." + md.getStatus());
+
+ list.add(pb);
+ Log.debug("ProjectRepository: added " + pb);
+ Log.debug("ProjectRepository: exports " + bb.getExports());
+ }
+ }
+
+ public static class ProjectBundle extends SigilBundle {
+ private String id;
+ private String module;
+ private String org;
+ private String revision;
+
+ @Override
+ public String toString() {
+ return "ProjectBundle[" + org + "@" + module + (id == null ? "" : "$" + id)
+ + "#" + revision + "]";
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String rev) {
+ this.revision = rev;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java
new file mode 100644
index 0000000..e04dc60
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/ProjectRepositoryProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+
+public class ProjectRepositoryProvider implements IRepositoryProvider{
+ private static HashMap<String, ProjectRepository> cache = new HashMap<String, ProjectRepository>();
+
+ public IBundleRepository createRepository(String id, Properties properties) throws RepositoryException {
+ ProjectRepository repository = cache.get(id);
+
+ if (repository == null) {
+ String pattern = properties.getProperty("pattern");
+ if (pattern == null) {
+ throw new RepositoryException("property 'pattern' not specified.");
+ }
+ repository = new ProjectRepository(id, pattern);
+ cache.put(id, repository);
+ }
+
+ return repository;
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java
new file mode 100644
index 0000000..1ab3772
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilParser.java
@@ -0,0 +1,587 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IResolution;
+
+public class SigilParser implements ModuleDescriptorParser {
+
+ private static DelegateParser instance;
+
+ // used by ProjectRepository
+ static DelegateParser instance() {
+ if (instance == null)
+ throw new IllegalStateException("SigilParser is not instantiated.");
+ return instance;
+ }
+
+ public SigilParser() {
+ if (instance == null) {
+ instance = new DelegateParser();
+ }
+ }
+
+ /**
+ * In IvyDE, IvyContext is not available, so we can't find the SigilResolver.
+ * This allows us to construct one.
+ * @deprecated temporary to support IvyDE
+ */
+ public void setConfig(String config) {
+ instance.config = config;
+ }
+
+ /**
+ * sets delegate parser.
+ * If not set, we delegate to the default Ivy parser.
+ * @param type name returned by desired parser's getType() method.
+ */
+ public void setDelegateType(String type) {
+ for (ModuleDescriptorParser parser : ModuleDescriptorParserRegistry.getInstance().getParsers()) {
+ if (parser.getType().equals(type)) {
+ instance.delegate = parser;
+ break;
+ }
+ }
+
+ if (instance.delegate == null) {
+ throw new IllegalArgumentException("Can't find parser delegateType=" + type);
+ }
+ }
+
+ /**
+ * sets default file name the delegate parser accepts.
+ * If not set, defaults to "ivy.xml".
+ * @param name
+ */
+ public void setDelegateFile(String name) {
+ instance.ivyFile = name;
+ }
+
+ /**
+ * sets the dependencies to keep from the delegate parser.
+ * If not set, all existing dependencies are dropped.
+ * @param regex pattern matching dependency names to keep.
+ */
+ public void setKeepDependencies(String regex) {
+ instance.keepDependencyPattern = Pattern.compile(regex);
+ }
+
+ /**
+ * reduce level of info logging.
+ * @param quiet
+ */
+ public void setQuiet(String quiet) {
+ instance.quiet = Boolean.parseBoolean(quiet);
+ }
+
+ /**
+ * sets the SigilResolver we use.
+ * If not set, we use the first SigilResolver we find.
+ * @param name
+ */
+ public void setResolver(String name) {
+ instance.resolverName = name;
+ }
+
+ /**
+ * adds override element: <override name="X" pattern="Y" replace="Z"/>. Overrides
+ * Ivy variables using a pattern substitution on the resource directory path.
+ *
+ * @deprecated
+ * This is only needed when a delegate parser expects Ant variables to be set
+ * during the ivy:buildlist task (or the ProjectRepository initialisation),
+ * which they are not. The delegate parser should really be fixed, as otherwise
+ * the ivy:buildlist task won't work without this workaround.
+ */
+ // e.g. <override name="ant.project.name" pattern=".*/([^/]+)/([^/]+)$" replace="$1-$2"/>
+ public void addConfiguredOverride(Map<String, String> var) {
+ final String name = var.get("name");
+ final String regex = var.get("pattern");
+ final String replace = var.get("replace");
+
+ if (name == null || regex == null || replace == null)
+ throw new IllegalArgumentException("override must contain name, pattern and replace attributes.");
+
+ instance.varRegex.put(name, Pattern.compile(regex));
+ instance.varReplace.put(name, replace);
+ }
+
+ // implement ModuleDescriptorParser interface by delegation to singleton instance.
+
+ public boolean accept(Resource res) {
+ return instance.accept(res);
+ }
+
+ public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
+ return instance.getMetadataArtifact(mrid, res);
+ }
+
+ public String getType() {
+ return instance.getType();
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
+ throws ParseException, IOException {
+ return instance.parseDescriptor(settings, xmlURL, validate);
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
+ throws ParseException, IOException {
+ return instance.parseDescriptor(settings, xmlURL, res, validate);
+ }
+
+ public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md) throws ParseException,
+ IOException {
+ instance.toIvyFile(source, res, destFile, md);
+ }
+
+ /**
+ * delegating parser.
+ * (optionally) removes original dependencies and augments with sigil-determined dependencies.
+ */
+ static class DelegateParser extends XmlModuleDescriptorParser {
+
+ private static final String SIGIL_BUILDLIST = IBldProject.PROJECT_FILE;
+ private static final String KEEP_ALL = ".*";
+
+ private IBldResolver resolver;
+ private ParserSettings defaultSettings;
+ private Map<String, DefaultModuleDescriptor> rawCache = new HashMap<String, DefaultModuleDescriptor>();
+ private Map<String, DefaultModuleDescriptor> augmentedCache = new HashMap<String, DefaultModuleDescriptor>();
+
+ private HashMap<String, String> varReplace = new HashMap<String, String>();
+ private HashMap<String, Pattern> varRegex = new HashMap<String, Pattern>();
+
+ private ModuleDescriptorParser delegate;
+ private String ivyFile = "ivy.xml";
+ private String resolverName;
+ private Pattern keepDependencyPattern;
+ private boolean quiet;
+ private String config;
+
+ @Override
+ public boolean accept(Resource res) {
+ boolean accept = (res instanceof SigilResolver.SigilIvy)
+ || res.getName().endsWith("/" + SIGIL_BUILDLIST)
+ || ((instance.getSigilURI(res) != null) &&
+ ((instance.delegate != null ? instance.delegate.accept(res) : false) || super.accept(res)));
+ if (accept)
+ Log.verbose("accepted: " + res);
+ return accept;
+ }
+
+ @Override
+ public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md)
+ throws ParseException, IOException {
+ Log.verbose("writing resource: " + res + " toIvyFile: " + destFile);
+ // source allows us to keep layout and comments,
+ // but this doesn't work if source is not ivy.xml.
+ // So just write file directly from descriptor.
+ try {
+ XmlModuleDescriptorWriter.write(md, destFile);
+ } finally {
+ if (source != null)
+ source.close();
+ }
+ }
+
+ @Override
+ public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
+ throws ParseException, IOException {
+ return parseDescriptor(settings, xmlURL, new URLResource(xmlURL), validate);
+ }
+
+ @Override
+ public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
+ throws ParseException, IOException {
+ String cacheKey = xmlURL.toString() + settings.hashCode();
+ DefaultModuleDescriptor dmd = augmentedCache.get(cacheKey);
+
+ if (dmd == null) {
+ dmd = rawCache.get(cacheKey);
+ if (dmd == null) {
+ dmd = delegateParse(settings, xmlURL, res, validate);
+ }
+
+ if (!quiet)
+ Log.info("augmenting module=" + dmd.getModuleRevisionId().getName() +
+ " ant.project.name=" + settings.substitute("${ant.project.name}"));
+
+ addDependenciesToDescriptor(res, dmd);
+ augmentedCache.put(cacheKey, dmd);
+
+ Log.verbose("augmented dependencies: " + Arrays.asList(dmd.getDependencies()));
+ }
+
+ return dmd;
+ }
+
+ // used by ProjectRepository
+ ModuleDescriptor parseDescriptor(URL projectURL) throws ParseException, IOException {
+ if (defaultSettings == null) {
+ Ivy ivy = IvyContext.getContext().peekIvy();
+ if (ivy == null)
+ throw new IllegalStateException("can't get default settings - no ivy context.");
+ defaultSettings = ivy.getSettings();
+ }
+
+ URL ivyURL = new URL(projectURL, ivyFile);
+ String cacheKey = ivyURL.toString() + defaultSettings.hashCode();
+ DefaultModuleDescriptor dmd = rawCache.get(cacheKey);
+
+ if (dmd == null) {
+ URLResource res = new URLResource(ivyURL);
+ // Note: this doesn't contain the augmented dependencies, which is OK,
+ // since the ProjectRepository only needs the id and status.
+ dmd = delegateParse(defaultSettings, ivyURL, res, false);
+ rawCache.put(cacheKey, dmd);
+ }
+
+ return dmd;
+ }
+
+ private DefaultModuleDescriptor delegateParse(ParserSettings settings, URL xmlURL, Resource res,
+ boolean validate) throws ParseException, IOException {
+ String resName = res.getName();
+
+ if (resName.endsWith("/" + SIGIL_BUILDLIST)) {
+ String name = resName.substring(0, resName.length() - SIGIL_BUILDLIST.length());
+ res = res.clone(name + ivyFile);
+ xmlURL = new URL(xmlURL, ivyFile);
+ }
+
+ if (settings instanceof IvySettings) {
+ IvySettings ivySettings = (IvySettings) settings;
+ String dir = new File(res.getName()).getParent();
+
+ for (String name : varRegex.keySet()) {
+ Pattern regex = varRegex.get(name);
+ String replace = varReplace.get(name);
+
+ String value = regex.matcher(dir).replaceAll(replace);
+
+ Log.debug("overriding variable " + name + "=" + value);
+ ivySettings.setVariable(name, value);
+ }
+ }
+
+ ModuleDescriptor md = null;
+ if (delegate == null || !delegate.accept(res)) {
+ md = super.parseDescriptor(settings, xmlURL, res, validate);
+ } else {
+ md = delegate.parseDescriptor(settings, xmlURL, res, validate);
+ }
+
+ return mungeDescriptor(md, res);
+ }
+
+ /**
+ * clones descriptor and removes dependencies, as descriptor MUST have
+ * 'this' as the parser given to its constructor.
+ * Only dependency names matched by keepDependencyPattern are kept,
+ * as we're going to add our own dependencies later.
+ */
+ private DefaultModuleDescriptor mungeDescriptor(ModuleDescriptor md, Resource res) {
+
+ if ((md instanceof DefaultModuleDescriptor) &&
+ (md.getParser() == this) &&
+ (KEEP_ALL.equals(keepDependencyPattern))) {
+ return (DefaultModuleDescriptor) md;
+ }
+
+ DefaultModuleDescriptor dmd = new DefaultModuleDescriptor(this, res);
+
+ dmd.setModuleRevisionId(md.getModuleRevisionId());
+ dmd.setPublicationDate(md.getPublicationDate());
+
+ for (Configuration c : md.getConfigurations()) {
+ String conf = c.getName();
+
+ dmd.addConfiguration(c);
+
+ for (Artifact a : md.getArtifacts(conf)) {
+ dmd.addArtifact(conf, a);
+ }
+ }
+
+ if (keepDependencyPattern != null) {
+ for (DependencyDescriptor dependency : md.getDependencies()) {
+ String name = dependency.getDependencyId().getName();
+ if (keepDependencyPattern.matcher(name).matches()) {
+ dmd.addDependency(dependency);
+ }
+ }
+ }
+
+ return dmd;
+ }
+
+ /*
+ * find URI to Sigil project file. This assumes that it is in the same
+ * directory as the ivy file.
+ */
+ private URI getSigilURI(Resource res) {
+ URI uri = null;
+
+ if (res instanceof URLResource) {
+ URL url = ((URLResource) res).getURL();
+ uri = URI.create(url.toString()).resolve(IBldProject.PROJECT_FILE);
+ try {
+ InputStream stream = uri.toURL().openStream(); // check file
+ // exists
+ stream.close();
+ } catch (IOException e) {
+ uri = null;
+ }
+ } else if (res instanceof FileResource) {
+ uri = ((FileResource) res).getFile().toURI().resolve(IBldProject.PROJECT_FILE);
+ if (!(new File(uri).exists()))
+ uri = null;
+ }
+
+ return uri;
+ }
+
+ /*
+ * add sigil dependencies to ModuleDescriptor.
+ */
+ private void addDependenciesToDescriptor(Resource res, DefaultModuleDescriptor md) throws IOException {
+ // FIXME: transitive should be configurable
+ final boolean transitive = true; // ivy default is true
+ final boolean changing = false;
+ final boolean force = false;
+
+ URI uri = getSigilURI(res);
+ if (uri == null)
+ return;
+
+ IBldProject project;
+
+ project = BldFactory.getProject(uri);
+
+ IBundleModelElement requirements = project.getDependencies();
+ Log.verbose("requirements: " + Arrays.asList(requirements.children()));
+
+ // preserve version range for Require-Bundle
+ // XXX: synthesise bundle version range corresponding to package version ranges?
+ HashMap<String, VersionRange> versions = new HashMap<String, VersionRange>();
+ for (IModelElement child : requirements.children()) {
+ if (child instanceof IRequiredBundle) {
+ IRequiredBundle bundle = (IRequiredBundle) child;
+ versions.put(bundle.getSymbolicName(), bundle.getVersions());
+ }
+ }
+
+ IBldResolver resolver = findResolver();
+ if (resolver == null) {
+ // this can happen in IvyDE, but it retries and is OK next time.
+ Log.warn("failed to find resolver - IvyContext not yet available.");
+ return;
+ }
+
+ IResolution resolution = resolver.resolve(requirements, false);
+ Log.verbose("resolution: " + resolution.getBundles());
+
+ ModuleRevisionId masterMrid = md.getModuleRevisionId();
+ DefaultDependencyDescriptor dd;
+ ModuleRevisionId mrid;
+
+ for (ISigilBundle bundle : resolution.getBundles()) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ String name = info.getSymbolicName();
+
+ if (name == null) {
+ // e.g. SystemProvider with framework=null
+ continue;
+ }
+
+ if (bundle instanceof ProjectRepository.ProjectBundle) {
+ ProjectRepository.ProjectBundle pb = (ProjectRepository.ProjectBundle) bundle;
+ String org = pb.getOrg();
+ if (org == null)
+ org = masterMrid.getOrganisation();
+ String id = pb.getId();
+ String module = pb.getModule();
+ String rev = pb.getRevision();
+
+ mrid = ModuleRevisionId.newInstance(org, module, rev);
+ dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+
+ if (!id.equals(module)) { // non-default artifact
+ dd.addDependencyArtifact(module,
+ new DefaultDependencyArtifactDescriptor(dd, id, "jar", "jar", null, null));
+ }
+ } else {
+ VersionRange version = versions.get(name);
+ String rev = version != null ? version.toString() : info.getVersion().toString();
+ mrid = ModuleRevisionId.newInstance(SigilResolver.ORG_SIGIL, name, rev);
+ dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+ }
+
+ int nDeps = 0;
+ boolean foundDefault = false;
+
+ // TODO: make dependency configurations configurable SIGIL-176
+ for (String conf : md.getConfigurationsNames()) {
+ if (conf.equals("default")) {
+ foundDefault = true;
+ } else if (md.getArtifacts(conf).length == 0) {
+ dd.addDependencyConfiguration(conf, conf + "(default)");
+ nDeps++;
+ }
+ }
+
+ if (nDeps > 0) {
+ if (foundDefault)
+ dd.addDependencyConfiguration("default", "default");
+ } else {
+ dd.addDependencyConfiguration("*", "*"); // all configurations
+ }
+
+ md.addDependency(dd);
+ }
+
+ boolean resolved = true;
+ for (IModelElement child : requirements.children()) {
+ ISigilBundle provider = resolution.getProvider(child);
+ if (provider == null) {
+ resolved = false;
+ // this is parse phase, so only log verbose message.
+ // error is produced during resolution phase.
+ Log.verbose("WARN: can't resolve: " + child);
+
+ String name;
+ String version;
+ if (child instanceof IRequiredBundle) {
+ IRequiredBundle rb = (IRequiredBundle) child;
+ name = rb.getSymbolicName();
+ version = rb.getVersions().toString();
+ } else {
+ IPackageImport pi = (IPackageImport) child;
+ name = "import!" + pi.getPackageName();
+ version = pi.getVersions().toString();
+ }
+
+ mrid = ModuleRevisionId.newInstance("!" + SigilResolver.ORG_SIGIL, name, version);
+ dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
+ dd.addDependencyConfiguration("*", "*"); // all
+ // configurations
+ md.addDependency(dd);
+ }
+ }
+
+ if (!resolved) {
+ // Ivy does not show warnings or errors logged from parse phase, until after resolution.
+ // but <buildlist> ant task doesn't do resolution, so errors would be silently ignored.
+ // Hence, we must use Message.info to ensure this failure is seen.
+ if (!quiet)
+ Log.info("WARN: resolution failed in: " + masterMrid.getName() + ". Use -v for details.");
+ }
+ }
+
+ /*
+ * find named resolver, or first resolver that implements IBldResolver.
+ */
+ private IBldResolver findResolver() {
+ if (resolver == null) {
+ Ivy ivy = IvyContext.getContext().peekIvy();
+ if (ivy != null) {
+ if (resolverName != null) {
+ DependencyResolver r = ivy.getSettings().getResolver(resolverName);
+ if (r == null) {
+ throw new Error("SigilParser: resolver \"" + resolverName + "\" not defined.");
+ } else if (r instanceof IBldResolver) {
+ resolver = (IBldResolver) r;
+ } else {
+ throw new Error("SigilParser: resolver \"" + resolverName + "\" is not a SigilResolver.");
+ }
+ } else {
+ for (Object r : ivy.getSettings().getResolvers()) {
+ if (r instanceof IBldResolver) {
+ resolver = (IBldResolver) r;
+ break;
+ }
+ }
+ }
+
+ if (resolver == null) {
+ throw new Error("SigilParser: can't find SigilResolver. Check ivysettings.xml.");
+ }
+ } else if (config != null) {
+ Log.warn("creating duplicate resolver to support IvyDE.");
+ resolver = new SigilResolver();
+ ((SigilResolver)resolver).setConfig(config);
+ }
+ }
+ return resolver;
+ }
+ }
+
+}
+
+/*
+ * this is only needed so that we can distinguish sigil-added dependencies from
+ * existing ones.
+ */
+class SigilDependencyDescriptor extends DefaultDependencyDescriptor {
+ public SigilDependencyDescriptor(DefaultModuleDescriptor md, ModuleRevisionId mrid, boolean force,
+ boolean changing, boolean transitive) {
+ super(md, mrid, force, changing, transitive);
+ }
+}
diff --git a/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java
new file mode 100644
index 0000000..596aaba
--- /dev/null
+++ b/sigil/bld-ivy/src/org/cauldron/bld/ivy/SigilResolver.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.ivy;
+
+import static java.lang.String.format;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.FileUtil;
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionException;
+
+/**
+ * This resolver is able to work with Sigil repositories.
+ * It does not allow publishing.
+ */
+public class SigilResolver extends BasicResolver implements IBldResolver {
+ /** the sigil-injected dependency organisation name */
+ static final String ORG_SIGIL = "sigil";
+
+ private IBldResolver resolver;
+ private String config;
+ private boolean extractBCP = false;
+ private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
+
+ /**
+ * no-args constructor required by Ivy.
+ *
+ * XXX: It doesn't currently seem to matter that Ivy instantiates this many times,
+ * since SigilParser caches it, and the ProjectRepositoryProvider static cache
+ * prevents it being re-loaded unnecessarily.
+ *
+ * If this should become a problem, then we will need to delegate to a singleton instance,
+ * as we have done in SigilParser.
+ */
+ public SigilResolver() {
+ }
+
+ public void setConfig(String config) {
+ this.config = config;
+ }
+
+ /**
+ * if true, resolver extracts any jars embedded in Bundle-ClassPath.
+ */
+ public void setExtractBCP(String extract) {
+ this.extractBCP = Boolean.parseBoolean(extract);
+ }
+
+ private IBldResolver getResolver() {
+ if (resolver == null) {
+ if (config == null) {
+ throw new Error("SigilResolver: not configured. Specify config=\"PATH\" in ivysettings.xml.");
+ }
+ try {
+ URI uri;
+ File file = new File(config);
+
+ if (file.isAbsolute()) {
+ uri = file.toURI();
+ } else {
+ URI cwd = new File(".").toURI();
+ uri = cwd.resolve(config);
+ }
+
+ Map<String, Properties> repositories = BldFactory.getConfig(uri).getRepositoryConfig();
+ resolver = new BldResolver(repositories);
+ } catch (IOException e) {
+ throw new Error("SigilResolver: failed to configure: " + e);
+ }
+ }
+ return resolver;
+ }
+
+ public IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException {
+ return getResolver().resolveOrFail(element, transitive);
+ }
+
+ public IResolution resolve(IModelElement element, boolean transitive) {
+ return getResolver().resolve(element, transitive);
+ }
+
+ public String getTypeName() {
+ return "sigil";
+ }
+
+ /*
+ * synthesize an ivy descriptor for a Sigil dependency. called after Sigil
+ * resolution, so descriptor already contains resolved version.
+ */
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ ResolvedResource ref = null;
+
+ ModuleRevisionId id = dd.getDependencyRevisionId();
+
+ if (!id.getOrganisation().equals(ORG_SIGIL)) {
+ return null;
+ }
+
+ ISigilBundle bundle = resolve(id);
+ if (bundle == null) {
+ return null;
+ }
+
+ String symbolicName = id.getName();
+ String version = bundle.getVersion().toString();
+
+ Resource res = new SigilIvy(extractBCP ? bundle : null, symbolicName, version);
+ ref = new ResolvedResource(res, version);
+
+ Log.debug(format("findIvyFileRef: dd=%s => ref=%s", dd, ref));
+ return ref;
+ }
+
+ @Override
+ protected ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+ String name = artifact.getName();
+ ModuleRevisionId id = artifact.getModuleRevisionId();
+
+ if (!id.getOrganisation().equals(ORG_SIGIL)) {
+ return null;
+ }
+
+ ISigilBundle bundle = resolve(id);
+ if (bundle == null) {
+ return null;
+ }
+
+ IBundleModelElement info = bundle.getBundleInfo();
+ URI uri = info.getUpdateLocation();
+ Resource res = null;
+
+ try {
+ URL url = (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+ if (name.contains("!")) {
+ String[] split = name.split("!");
+ url = new URL("jar:" + url + "!/" + split[1] + "." + artifact.getExt());
+ }
+ res = new URLResource(url);
+ } catch (MalformedURLException e) {
+ System.out.println("Oops! " + e);
+ }
+
+ String version = bundle.getVersion().toString();
+ ResolvedResource ref = new ResolvedResource(res, version);
+
+ Log.debug(format("findArtifactRef: artifact=%s, date=%s => ref=%s", artifact, date, ref));
+ return ref;
+ }
+
+ private ISigilBundle resolve(ModuleRevisionId id) {
+ String revision = id.getRevision();
+ String range = revision;
+
+ if (revision.indexOf(',') < 0) {
+ // SigilParser has already resolved the revision from the import
+ // version range.
+ // We now need to locate the same bundle to get its download URL.
+ // We must use an OSGi version range to specify the exact version,
+ // otherwise it will resolve to "specified version or above".
+ range = "[" + revision + "," + revision + "]";
+ }
+
+ RequiredBundle bundle = new RequiredBundle();
+ bundle.setSymbolicName(id.getName());
+ bundle.setVersions(VersionRange.parseVersionRange(range));
+
+ try {
+ IResolution resolution = resolveOrFail(bundle, false);
+ ISigilBundle[] bundles = resolution.getBundles().toArray(new ISigilBundle[0]);
+ if (bundles.length == 1) {
+ return bundles[0];
+ }
+ } catch (ResolutionException e) {
+ return null;
+ }
+ return null;
+ }
+
+ /*
+ * Implement BasicResolver abstract methods
+ */
+
+ @Override
+ protected long get(Resource res, File dest) throws IOException {
+ FileUtil.copy(res.openStream(), dest, null);
+ long len = res.getContentLength();
+ Log.debug(format("get(%s, %s) = %d", res, dest, len));
+ return len;
+ }
+
+
+ @Override
+ public Resource getResource(String source) throws IOException {
+ source = encode(source);
+ Resource res = resourcesCache.get(source);
+ if (res == null) {
+ res = new URLResource(new URL(source));
+ resourcesCache.put(source, res);
+ }
+ Log.debug(format("SIGIL: getResource(%s) = %d", source, res));
+ return res;
+ }
+
+ private static String encode(String source) {
+ return source.trim().replaceAll(" ", "%20");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Collection findNames(Map tokenValues, String token) {
+ throw new Error("SigilResolver: findNames not supported.");
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+ throw new Error("SigilResolver: publish not supported.");
+ }
+
+ public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
+ // stop publish attempts being silently ignored.
+ throw new Error("SigilResolver: publish not supported.");
+ }
+
+ /*
+ * Synthesize a virtual ivy file for a Sigil dependency.
+ */
+ static class SigilIvy implements Resource {
+ private StringBuilder content = new StringBuilder();
+ private String name;
+ private boolean exists = true;
+
+ private SigilIvy() {
+ }
+
+ public SigilIvy(ISigilBundle bundle, String module, String rev) {
+ this.name = "sigil!" + module + "#" + rev;
+
+ String org = ORG_SIGIL;
+ // FIXME: make status and pub configurable
+ String status = "release";
+ String pub = "20080912162859";
+
+ content.append("<ivy-module version=\"1.0\">\n");
+
+ content.append(format(
+ "<info organisation=\"%s\" module=\"%s\" revision=\"%s\" status=\"%s\" publication=\"%s\"/>\n",
+ org, module, rev, status, pub));
+
+ String bcp = readBundleClassPath(bundle);
+ if (bcp != null) {
+ content.append("<publications>\n");
+ for (String j : bcp.split(",\\s*")) {
+ if (j.equals(".")) {
+ content.append("<artifact/>\n");
+ } else if (!j.endsWith("component-activator.jar")) {
+ if (j.endsWith(".jar"))
+ j = j.substring(0, j.length() - 4);
+ content.append(format("<artifact name=\"%s!%s\"/>\n", module, j));
+ }
+ }
+ content.append("</publications>\n");
+ }
+
+ // TODO: add dependencies?
+ // <dependencies>
+ // <dependency org="org.apache" name="log4j" rev="1.2.12"
+ // revConstraint="[1.2,1.3)"/>
+ // </dependencies>
+
+ content.append("</ivy-module>\n");
+ }
+
+ private String readBundleClassPath(ISigilBundle bundle) {
+ if (bundle == null)
+ return null;
+
+ URI uri = bundle.getBundleInfo().getUpdateLocation();
+ InputStream is = null;
+ try {
+ URL url = (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+ is = url.openStream();
+ JarInputStream js = new JarInputStream(is, false);
+ Manifest m = js.getManifest();
+ if (m != null)
+ return (String) m.getMainAttributes().getValue("Bundle-ClassPath");
+ } catch (IOException e) {
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e2) {
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public String toString() {
+ return "SigilIvy[" + name + "]";
+ }
+
+ // clone is used to read checksum files
+ // so clone(getName() + ".sha1").exists() should be false
+ public Resource clone(String cloneName) {
+ Log.debug("SigilIvy: clone: " + cloneName);
+ SigilIvy clone = new SigilIvy();
+ clone.name = cloneName;
+ clone.exists = false;
+ return clone;
+ }
+
+ public boolean exists() {
+ return exists;
+ }
+
+ public long getContentLength() {
+ return content.length();
+ }
+
+ public long getLastModified() {
+ // TODO Auto-generated method stub
+ Log.debug("NOT IMPLEMENTED: getLastModified");
+ return 0;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+
+ @SuppressWarnings("deprecation")
+ public InputStream openStream() throws IOException {
+ return new java.io.StringBufferInputStream(content.toString());
+ }
+ }
+}
diff --git a/sigil/bld-ivy/test/config/proj1/newton-project.xml b/sigil/bld-ivy/test/config/proj1/newton-project.xml
new file mode 100644
index 0000000..98a52cd
--- /dev/null
+++ b/sigil/bld-ivy/test/config/proj1/newton-project.xml
@@ -0,0 +1,35 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<sigil:bundle xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<classpathEntry><classpathentry kind="lib" path="lib/dependee.jar"/></classpathEntry>
+<classpathEntry><classpathentry kind="src" path="src"/></classpathEntry>
+<bundle>
+<bundle-version>0.0.0</bundle-version>
+<bundle-vendor>Paremus Limited</bundle-vendor>
+<bundle-symbolicName>org.example.helloworld</bundle-symbolicName>
+<fragment-host bundle="com.springsource.org.apache.commons.lang" version="[2.0.0,2.4.0)"/>
+<export-package package="org.example.helloworld.api" version="0.0.0"/><import-package package="org.cauldron.newton.command" version="1.2.0"/><import-package package="org.apache.log4j" version="[1.2.14,1.3)"/><import-package package="org.example.helloworld.api" version="0"/><import-package package="org.cauldron.newton.command.console" version="1.2.0"/></bundle>
+<sigil:composite xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<location>sca/org.example.helloworld.service.composite</location>
+</sigil:composite>
+<sigil:composite xmlns:sigil="http://sigil.codecauldron.org/xml/sigil-namespace">
+<location>sca/org.example.helloworld.cli.composite</location>
+</sigil:composite>
+</sigil:bundle>
diff --git a/sigil/bld-ivy/test/config/proj1/sigil.properties b/sigil/bld-ivy/test/config/proj1/sigil.properties
new file mode 100644
index 0000000..424cd8d
--- /dev/null
+++ b/sigil/bld-ivy/test/config/proj1/sigil.properties
@@ -0,0 +1,33 @@
+# proj1 sigil.properties
+
+version: 1.2.3.beta1
+
+-bundles: main
+
+-exports: standalone
+
+-imports: \
+ javax.swing, \
+ javax.servlet, \
+ org.apache.commons.logging, \
+ org.apache.commons.lang, \
+ org.apache.log4j, \
+
+ximports: \
+ !org.cauldron.newton.framework, \
+ !org.osgi.*, \
+ org.apache.log4j;version="1.2.3", \
+
+-fragment: \
+ com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)"
+
+-composites: \
+ ivy.xml
+
+-resources: \
+ version.properties
+
+header;Bundle-Vendor: Paremus Limited
+header;Random-Heading: this header is a bit like random
+
+# end
diff --git a/sigil/bld-ivy/test/config/sigil-defaults.properties b/sigil/bld-ivy/test/config/sigil-defaults.properties
new file mode 100644
index 0000000..784ef8e
--- /dev/null
+++ b/sigil/bld-ivy/test/config/sigil-defaults.properties
@@ -0,0 +1,6 @@
+# sigil defaults
+
+package;javax.servlet: (2.4,3.0]
+package;org.apache.log4j: [1.2.14,1.3)
+package;org.apache.commons.lang: [2.0.0,2.4.0)
+
diff --git a/sigil/bld-ivy/test/dependence/build.xml b/sigil/bld-ivy/test/dependence/build.xml
new file mode 100644
index 0000000..acb37e9
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/build.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="dependence" default="clean">
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean directories">
+ <delete includeemptydirs="true">
+ <fileset dir="settings" excludes="ivysettings.*, sigil*.properties" />
+ </delete>
+ <ant dir="dependee" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ </target>
+
+ <!-- =================================
+ target: all
+ ================================= -->
+ <target name="all" depends="clean" description="--> make the whole example of dependency">
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/test/dependence/dependee/build.xml b/sigil/bld-ivy/test/dependence/dependee/build.xml
new file mode 100644
index 0000000..925d95a
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/build.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="dependee" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <property name="ivy.jar.file"
+ value="/opt/apache-ivy-2.0.0/ivy-2.0.0.jar"/>
+
+ <!-- =================================
+ target: init
+ ================================= -->
+ <target name="init">
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar.file}"/>
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${basedir}/../../../target/sigil-ivy-plugin.jar"/>
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="init"
+ description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="standalone.Main"/>
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="compile" description="--> make a jar file for this project">
+ <propertyfile file="${classes.dir}/version.properties">
+ <entry key="version" type="int" operation="+" default="0" />
+ </propertyfile>
+ <property file="${classes.dir}/version.properties" />
+
+ <!--
+ <jar destfile="${build.dir}/${ant.project.name}.jar">
+ <fileset dir="${classes.dir}" />
+ </jar>
+ -->
+
+ <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+ classpathref="run.path.id" />
+
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="jar" description="--> publish this project in the ivy repository">
+ <property name="revision" value="${version}"/>
+ <delete file="${build.dir}/ivy.xml"/>
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="projects"
+ pubrevision="${revision}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${revision}" />
+ </target>
+
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name="sigil.properties" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/sigil/bld-ivy/test/dependence/dependee/ivy.xml b/sigil/bld-ivy/test/dependence/dependee/ivy.xml
new file mode 100644
index 0000000..b250663
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/ivy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="dependee"/>
+ <dependencies>
+ <!--
+ <dependency org="sigil" name="com.springsource.org.apache.log4j" rev="[1.2,1.3)"/>
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/dependence/dependee/sigil.properties b/sigil/bld-ivy/test/dependence/dependee/sigil.properties
new file mode 100644
index 0000000..9dd7d8a
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/sigil.properties
@@ -0,0 +1,25 @@
+# dependee sigil.properties
+# all initial comments are retained.
+
+version: 1.2.3.beta1
+
+-bundles: dependee
+
+-exports: standalone
+
+-imports: \
+ org.apache.commons.lang;version="[2.0.0,2.4.0)";resolve=compile;resolution=optional, \
+ org.apache.commons.logging, \
+ javax.servlet;version="(2.4,3.0]", \
+ org.apache.log4j;version="[1.2.14,1.3)", \
+ javax.swing, \
+
+-resources: \
+ version.properties
+
+header;Hello: World
+
+X-requires: \
+ com.springsource.org.apache.commons.lang;version="[2.0.0,2.4.0)", \
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java b/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java
new file mode 100644
index 0000000..12ffa27
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/dependee/src/standalone/Main.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package standalone;
+
+import java.util.Properties;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * TODO write javadoc
+ */
+public class Main {
+ /**
+ * Returns the version of the project
+ * @return a string representation of the version, null if the version could not be retreived
+ */
+ public static String getVersion() {
+ Properties p = new Properties();
+ try {
+ p.load(Main.class.getResourceAsStream("/version.properties"));
+ String version = p.getProperty("version");
+ if (version != null) {
+ return String.valueOf(Integer.parseInt(version));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Return the same string with all words capitalized.
+ * @param str the string conatining the words to capitalize
+ * @return null if the string was null, the string with all words capitalized otherwise
+ */
+ public static String capitalizeWords(String str) {
+ System.out.println(" [" + Main.class.getName() + "] capitalizing string \"" + str + "\" using " + WordUtils.class.getName());
+ return WordUtils.capitalizeFully(str);
+ }
+ public static void main(String[] args) {
+ String message="sentence to capitalize";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + capitalizeWords(message));
+ }
+}
diff --git a/sigil/bld-ivy/test/dependence/depender/build.xml b/sigil/bld-ivy/test/dependence/depender/build.xml
new file mode 100644
index 0000000..01216b7
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/build.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="depender" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <property name="ivy.jar.file"
+ value="/opt/apache-ivy-2.0.0/ivy-2.0.0.jar"/>
+
+ <!-- =================================
+ target: init
+ ================================= -->
+ <target name="init">
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar.file}"/>
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="init"
+ description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}" dot="true"/>
+ </target>
+
+ <!-- =================================
+ target: gen-graph
+ ================================= -->
+ <target name="gen-graph" depends="report" description="--> generates a graph of dependencies (requires dot in your path - see http://www.graphviz.org/)">
+ <property name="dot.file" value="${build.dir}/apache-depending-default.dot" />
+ <property name="ivygraph.output.file" value="${build.dir}/graph.png" />
+ <exec executable="dot">
+ <arg line="-T png -o ${ivygraph.output.file} ${dot.file}" />
+ </exec>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="clean, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="depending.Main"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name="sigil.properties" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/sigil/bld-ivy/test/dependence/depender/ivy.xml b/sigil/bld-ivy/test/dependence/depender/ivy.xml
new file mode 100644
index 0000000..f825b21
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/ivy.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="depender"/>
+ <dependencies>
+ <!--
+ <dependency name="dependee" rev="latest.integration" />
+ -->
+ </dependencies>
+ <conflicts>
+ <manager name="latest-compatible"/>
+ </conflicts>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/dependence/depender/sigil.properties b/sigil/bld-ivy/test/dependence/depender/sigil.properties
new file mode 100644
index 0000000..e76ec83
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/sigil.properties
@@ -0,0 +1,7 @@
+# depender sigil.properties
+
+-bundles: depender
+
+-imports: standalone
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java b/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java
new file mode 100644
index 0000000..db1b208
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/depender/src/depending/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package depending;
+
+/**
+ * TODO write javadoc
+ */
+public class Main {
+ public static void main(String[] args) {
+ String standaloneVersion = standalone.Main.getVersion();
+ if (standaloneVersion!=null) {
+ System.out.println("you are using version " + standaloneVersion + " of class " + standalone.Main.class.getName());
+ } else {
+ System.err.println("failed to get version of " + standalone.Main.class.getName());
+ }
+ String message = "i am " + Main.class.getName() + " and " + standalone.Main.class.getName() + " will do the job for me";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + standalone.Main.capitalizeWords(message));
+ }
+}
diff --git a/sigil/bld-ivy/test/dependence/settings/ivysettings.properties b/sigil/bld-ivy/test/dependence/settings/ivysettings.properties
new file mode 100644
index 0000000..f68f415
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/ivysettings.properties
@@ -0,0 +1,19 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+repository.dir=${ivy.settings.dir}/repository
diff --git a/sigil/bld-ivy/test/dependence/settings/ivysettings.xml b/sigil/bld-ivy/test/dependence/settings/ivysettings.xml
new file mode 100644
index 0000000..9312a85
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/ivysettings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/ivysettings.properties"/>
+ <settings defaultResolver="sigil-libs"/>
+
+ <!--<caches defaultCacheDir="${ivy.settings.dir}/ivy-cache" />
+ -->
+ <caches default="mycache" checkUpToDate="false">
+ <cache name="mycache" basedir="${ivy.settings.dir}/ivy-cache" useOrigin="true"/>
+ </caches>
+
+ <classpath file="${ivy.settings.dir}/../../../target/sigil-ivy-plugin.jar" />
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser/>
+ </parsers>
+
+ <resolvers>
+ <sigil name="sigil-libs" config="${ivy.settings.dir}/sigil-repos.properties" />
+ <filesystem name="projects">
+ <ivy pattern="${repository.dir}/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/[artifact]-[revision].[ext]" />
+ </filesystem>
+</resolvers>
+<modules>
+ <module organisation="org.apache" name="dependee" resolver="projects"/>
+</modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties b/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties
new file mode 100644
index 0000000..f00cfaf
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/settings/sigil-repos.properties
@@ -0,0 +1,17 @@
+# repository config
+
+-repositories: system, project, spring
+
+system;provider: system
+system;level: -1
+
+project;provider: project
+project;level: 0
+project;pattern: ${..}/*/[sigilproject]
+
+spring;provider: obr
+spring;level: 2
+spring;url: http://sigil.codecauldron.org/spring-external.obr
+spring;index: ${..}/settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/test/dependence/sigil-defaults.properties b/sigil/bld-ivy/test/dependence/sigil-defaults.properties
new file mode 100644
index 0000000..2bc4bc9
--- /dev/null
+++ b/sigil/bld-ivy/test/dependence/sigil-defaults.properties
@@ -0,0 +1,2 @@
+header;Bundle-Vendor: Paremus Limited
+version: 1.2.3.parent
diff --git a/sigil/bld-ivy/test/multi-project/build.xml b/sigil/bld-ivy/test/multi-project/build.xml
new file mode 100644
index 0000000..fd2d774
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/build.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="all" default="order" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <property name="common.dir" value="${basedir}/common" />
+ <import file="${common.dir}/common.xml"/>
+
+ <fileset id="projects" dir="projects" includes="**/build.xml"/>
+
+ <path id="unordered-list">
+ <fileset refid="projects"/>
+ </path>
+
+ <target name="buildlist" depends="load-ivy">
+ <ivy:buildlist reference="ordered-list">
+ <fileset refid="projects"/>
+ </ivy:buildlist>
+ </target>
+
+ <target name="publish" depends="buildlist"
+ description="compile, jar and publish all projects in the right order">
+ <subant target="publish" buildpathref="ordered-list">
+ <propertyset>
+ <propertyref name="ivy.loaded" />
+ </propertyset>
+ </subant>
+ </target>
+
+ <target name="clean" description="clean all projects">
+ <subant target="clean" buildpathref="unordered-list" />
+ </target>
+
+ <target name="fullclean" depends="clean, load-ivy"
+ description="clean tutorial: delete repository, ivy cache, and all projects">
+ <delete dir="repository"/>
+ <ivy:cleancache />
+ </target>
+
+ <target name="order" depends="buildlist">
+ <subant target="hello" buildpathref="ordered-list" />
+ </target>
+
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/common/build.properties b/sigil/bld-ivy/test/multi-project/common/build.properties
new file mode 100644
index 0000000..9552b4a
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/build.properties
@@ -0,0 +1,30 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+lib.dir = ${basedir}/lib
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+src.dir = ${basedir}/src
+repository.dir=${common.dir}/../repository
+
+ivy.file = ${basedir}/ivy.xml
+
+jar.file = ${build.dir}/${ant.project.name}.jar
+main.class.name = ${ant.project.name}.Main
+
+module.version.target = 1.0
diff --git a/sigil/bld-ivy/test/multi-project/common/common.xml b/sigil/bld-ivy/test/multi-project/common/common.xml
new file mode 100644
index 0000000..4e54d1d
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/common.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- a sample common ant build file, used for ivy multi-project tutorial
+ feel free to copy and adapt it to your own needs
+ Note that the only targets specific to ivy are:
+ load-ivy
+ resolve
+ report
+ ivy-new-version
+ publish
+ publish-local
+
+ All other targets are usual ant based targets, which could have been written
+ in a build not depending at all on ivy:
+ resolve constructs a lib directory based upon ivy dependencies, and then the lib dir
+ is used as in any classical ant build
+ -->
+
+ <property file="${common.dir}/build.properties"/>
+
+ <property name="ivy.jar.dir" value="/opt/apache-ivy-2.0.0-rc2" />
+ <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-2.0.0-rc2.jar" />
+ <property name="sigil-ivy-plugin.jar" value="${common.dir}/../../../target/sigil-ivy-plugin.jar"/>
+
+ <target name="hello">
+ <echo message="${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: load-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy 2.0 in your ant lib, you can simply remove this
+ target
+ ================================= -->
+ <path id="ivy.lib.path">
+ <path location="${ivy.jar.file}"/>
+ <path location="${sigil-ivy-plugin.jar}"/>
+ <!--
+ <path location="${user.home}/src/tmp/ivy/build/ivy.jar"/>
+ -->
+ </path>
+
+ <target name="load-ivy" depends="ivy-taskdefs">
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ </target>
+
+ <target name="ivy-taskdefs" unless="ivy.loaded">
+ <property name="ivy.loaded" value="true"/>
+ <echo message="Loading Ivy ..."/>
+ <!--
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant" classpath="${ivy.jar.file}"/>
+ -->
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ loaderRef="ivyLoader"
+ classpath="${ivy.jar.file}"/>
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}:${bndlib.jar}"/>
+ </target>
+
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+
+ <!-- setup ivy default configuration with some custom info -->
+ <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+ <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+ <!-- here is how we would have configured ivy if we had our own ivysettings file
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ -->
+
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="clean-lib, load-ivy" description="--> resolve and retrieve dependencies with ivy">
+ <mkdir dir="${lib.dir}"/> <!-- not usually necessary, ivy creates the directory IF there are dependencies -->
+
+ <!-- the call to resolve is not mandatory, retrieve makes an implicit call if we don't -->
+ <ivy:resolve file="${ivy.file}"/>
+ <ivy:retrieve pattern="${lib.dir}/[artifact].[ext]" />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> compile the project">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" debug="true" />
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="version, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="${main.class.name}"/>
+ </target>
+
+ <target name="ivy-new-version" depends="load-ivy" unless="ivy.new.revision">
+ <!-- default module version prefix value -->
+ <property name="module.version.prefix" value="${module.version.target}-dev-b" />
+
+ <!-- asks to ivy an available version number -->
+ <ivy:info file="${ivy.file}" />
+ <ivy:buildnumber
+ organisation="${ivy.organisation}" module="${ivy.module}"
+ revision="${module.version.prefix}" defaultBuildNumber="1" revSep=""/>
+ </target>
+
+ <target name="local-version">
+ <tstamp>
+ <format property="now" pattern="yyyyMMddHHmmss"/>
+ </tstamp>
+ <property name="ivy.new.revision" value="${module.version.target}-local-${now}"/>
+ </target>
+
+ <target name="version" depends="ivy-new-version">
+ <!-- create version file in classpath for later inclusion in jar -->
+ <mkdir dir="${classes.dir}"/>
+ <echo message="version=${ivy.new.revision}" file="${classes.dir}/${ant.project.name}.properties" append="false" />
+
+ <!-- load generated version properties file -->
+ <property file="${classes.dir}/${ant.project.name}.properties" />
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="version, compile" description="--> make a jar file for this project">
+
+ <!--
+ <jar destfile="${jar.file}">
+ <fileset dir="${classes.dir}" />
+ <manifest>
+ <attribute name="Built-By" value="${user.name}"/>
+ <attribute name="Build-Version" value="${version}" />
+ </manifest>
+ </jar>
+ -->
+
+ <sigil.bundle destpattern="${build.dir}/[id].[ext]"
+ classpathref="run.path.id"/>
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="clean-build, jar" description="--> publish this project in the ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="shared"
+ pubrevision="${version}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: publish-local
+ ================================= -->
+ <target name="publish-local" depends="local-version, jar" description="--> publish this project in the local ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${version}"
+ pubdate="${now}"
+ status="integration"
+ forcedeliver="true"
+ />
+ <echo message="project ${ant.project.name} published locally with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local" description="--> cleans the local repository for the current module">
+ <delete dir="${ivy.local.default.root}/${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-lib
+ ================================= -->
+ <target name="clean-lib" description="--> clean the project libraries directory (dependencies)">
+ <delete includeemptydirs="true" dir="${lib.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-build
+ ================================= -->
+ <target name="clean-build" description="--> clean the project built files">
+ <delete includeemptydirs="true" dir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" depends="clean-build, clean-lib" description="--> clean the project" />
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/common/ivysettings.xml b/sigil/bld-ivy/test/multi-project/common/ivysettings.xml
new file mode 100644
index 0000000..03440c4
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/ivysettings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/build.properties"/>
+ <caches defaultCacheDir="${ivy.settings.dir}/../ivy-cache" />
+
+ <settings defaultResolver="shared"
+ circularDependencyStrategy="error" />
+
+ <classpath file="${ivy.settings.dir}/../../../target/sigil-ivy-plugin.jar" />
+ <!--
+ Ant tasks "subant" and "ant" cause IvySettings to be re-loaded,
+ which then re-loads SigilParser in a new URLClassLoader,
+ which causes ProjectRepository to be re-initialised. Outch!
+
+ Better if Ivy classpath cached its classloader, or provided a
+ loaderRef attribute, like ant does.
+
+ Loading sigil-ivy-plugin.jar in ivy-taskdefs, uses the same ClassLoader
+ to load sigil-ivy-plugin, and thus avoids re-initialising ProjectRepositiory.
+ -->
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser quiet="true"/>
+ </parsers>
+
+ <resolvers>
+ <sigil name="sigil" config="${ivy.settings.dir}/sigil-repos.properties" />
+ <filesystem name="local">
+ <ivy pattern="${repository.dir}/local/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/local/[artifact]-[revision].[ext]" />
+ </filesystem>
+ <filesystem name="shared">
+ <ivy pattern="${repository.dir}/shared/[module]-[revision].xml" />
+ <artifact pattern="${repository.dir}/shared/[module]/[artifact]-[revision].[ext]" />
+ </filesystem>
+ </resolvers>
+ <modules>
+ <!--<module organisation="org.apache.ivy.example" name=".*" resolver="shared"/>
+ -->
+ <module organisation="sigil" resolver="sigil"/>
+ </modules>
+</ivysettings>
diff --git a/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh b/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh
new file mode 100755
index 0000000..2f7e184
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/sigil-gen.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# sigilgen build.xml ..
+
+function a2s() {
+ find ${1:?} -name '*.java' -o -name '*.spring' -o -name '*.sca' |
+ perl -e '
+ use strict;
+ use FileHandle;
+
+ my %imports;
+ my %exports;
+
+ while (<>) {
+ chomp($_);
+ my $path = $_;
+ my $fh = new FileHandle($path);
+
+ my $testFile = ($path =~ m!Test[^/]+$!);
+
+ while (my $line = <$fh>) {
+ chomp($line);
+
+ if ($line =~ /^\s*package\s+([\w.]+)\s*;\s*$/) {
+ my $exp = $1;
+ if (!($testFile && ($exp !~ /^test[.]/))) {
+ $exports{$exp} = 1;
+ }
+ }
+ elsif ($line =~ /^\s*import\s+([a-z_0-9.]+)([.][A-Z][^.]+)+;\s*$/) {
+ my $imp = $1;
+ $imports{$imp} = 1 unless ($imp =~ /^java[.]/);
+ }
+ elsif (($line !~ m!^\s*(//|\*|\@)!) &&
+ ($line =~ /(\W)((([a-z0-9]{2,})[.]){3,})[A-Z]/)) {
+ my $quot = $1;
+ my $imp = $2;
+ $imp =~ s/.$//;
+
+ if (($imp !~ /^java[.]/) &&
+ ($quot ne "\"" || ($path !~ /[.]java$/))) {
+ $imports{$imp} = 1;
+ }
+ }
+ }
+ $fh->close();
+ }
+
+ if (keys(%exports) == () && keys(%imports) == ()) {
+ exit(1);
+ }
+
+ print "-exports: \\\n";
+ foreach my $key (sort keys(%exports)) {
+ print "\t$key, \\\n";
+ }
+ print "\n";
+
+ print "-imports: \\\n";
+ foreach my $key (sort keys(%imports)) {
+ print "\t$key, \\\n";
+ }
+ print "\n";
+
+ exit(0);
+ '
+ return $?
+}
+
+for file in $*; do
+ echo "converting $file"
+ name=$(perl -ne 'print $1 if (/project name="([^"]*)"/);' $file)
+ dir=$(dirname $file)
+
+ if [ -f $dir/sigil.properties ]; then
+ rm -f $dir/sigil.properties.old
+ mv $dir/sigil.properties $dir/sigil.properties.old
+ fi
+
+ (
+ echo "# generated by sigilgen on $(date)"
+ echo
+ echo "-bundles: $name"
+ echo
+ a2s $dir && mv $dir/sigil.properties-part $dir/sigil.properties
+ ) > $dir/sigil.properties-part
+
+ rm -f $dir/sigil.properties-part
+
+done
diff --git a/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties b/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties
new file mode 100644
index 0000000..d98b618
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/common/sigil-repos.properties
@@ -0,0 +1,26 @@
+
+# repository config
+
+-repositories: system, project, newton, spring
+
+system;provider: system
+system;level: -1
+#system;framework: osgi.core.jar
+#system;profile: J2SE-1.5
+
+project;provider: project
+project;level: 0
+project;pattern: ../projects/**/[sigilproject]
+
+newton;provider: filesystem
+newton;level: 1
+newton;recurse: true
+#newton;dir: /opt/newton-1.2.5/lib
+newton;dir: /Users/derek/devl/Newton/src/target/newton-dist.dir/lib
+
+spring;provider: obr
+spring;level: 2
+spring;url: http://sigil.codecauldron.org/spring-external.obr
+spring;index: ../settings/spring-external.obr
+
+# end
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/build.properties b/sigil/bld-ivy/test/multi-project/projects/console/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/build.xml b/sigil/bld-ivy/test/multi-project/projects/console/build.xml
new file mode 100644
index 0000000..5c02cba
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="console" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml
new file mode 100644
index 0000000..93b5ee5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/ivy.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="console"
+ status="integration"/>
+ <dependencies>
+ <!--
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->standalone" />
+ <dependency name="find" rev="latest.integration" conf="default->standalone" />
+ <dependency name="sizewhere" rev="latest.integration" conf="default->standalone" />
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties
new file mode 100644
index 0000000..1d4bb13
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/sigil.properties
@@ -0,0 +1,6 @@
+
+-bundles: console
+
+-exports: console
+
+-requires: org.example.list
diff --git a/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java b/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java
new file mode 100644
index 0000000..678f3ca
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/console/src/console/Main.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package console;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Arrays;
+import java.lang.reflect.Method;
+
+
+public class Main {
+ private static Collection QUIT_COMMANDS = Arrays.asList(new String[] {"quit", "q", "exit"});
+ private static Collection HELP_COMMANDS = Arrays.asList(new String[] {"help", "h", "?"});
+
+ public static void main(String[] a) throws Exception {
+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+ while (true) {
+ System.out.print("> ");
+ String command = in.readLine().trim();
+ if (QUIT_COMMANDS.contains(command)) {
+ return;
+ }
+ if (HELP_COMMANDS.contains(command)) {
+ help();
+ continue;
+ }
+ String[] split = command.split(" ");
+ if (split.length == 0) {
+ error(command);
+ continue;
+ }
+
+ try {
+ String[] args = new String[split.length - 1];
+ System.arraycopy(split, 1, args, 0, args.length);
+ Class cl = Class.forName(split[0]+".Main");
+ Method m = cl.getMethod("main", new Class[] {String[].class});
+ m.invoke(null, new Object[] {args});
+ } catch (Exception ex) {
+ error(command);
+ continue;
+ }
+ }
+ }
+
+ private static void help() {
+ System.out.println("available commands:");
+ System.out.println("\tquit: quit the console");
+ System.out.println("\thelp: displays this message");
+ System.out.println("\tlist -dir <dir>: list files in given directory");
+ System.out.println("\tfind -dir <dir> -name <name>: list files with given name in given dir");
+ System.out.println("\tsizewhere -dir <dir> -name <name>: compute total size of files with given name in given dir");
+ System.out.println("\thelp: displays this message");
+ }
+
+ private static void error(String command) {
+ System.out.println("unknown command "+command);
+ System.out.println("type ? for help");
+ }
+
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/build.properties b/sigil/bld-ivy/test/multi-project/projects/find/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/build.xml b/sigil/bld-ivy/test/multi-project/projects/find/build.xml
new file mode 100644
index 0000000..4d23bf5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="find" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml
new file mode 100644
index 0000000..8026113
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/ivy.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="find"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="find" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <!--
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="list" rev="latest.integration" conf="core" />
+ <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties
new file mode 100644
index 0000000..30c10a7
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: find
+
+-exports: find
+
+-imports:\
+ version, \
+ list, \
+ org.apache.commons.collections, \
+ org.apache.commons.cli
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java b/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java
new file mode 100644
index 0000000..25fa1af
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/src/find/FindFile.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import version.Version;
+import list.ListFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Predicate;
+
+public class FindFile {
+ static {
+ Version.register("find");
+ }
+
+ public static Collection find(File dir, String name) {
+ return find(ListFile.list(dir), name);
+ }
+
+ private static Collection find(Collection files, final String name) {
+ return CollectionUtils.select(files, new Predicate() {
+ public boolean evaluate(Object o) {
+ return ((File)o).getName().indexOf(name) != -1;
+ }
+ });
+ }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java b/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java
new file mode 100644
index 0000000..a41c136
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/find/src/find/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package find;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName("dir")
+ .hasArg()
+ .withDescription("list files in given dir")
+ .create("dir");
+ Option name = OptionBuilder.withArgName("name")
+ .hasArg()
+ .withDescription("list files with given name")
+ .create("name");
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse(options, args);
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ Collection files = FindFile.find(dir, name);
+ System.out.println("listing files in " + dir + " containing " + name);
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ System.out.println("\t" + it.next() + "\n");
+ }
+ } catch(ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("find", options);
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/build.properties b/sigil/bld-ivy/test/multi-project/projects/list/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/build.xml b/sigil/bld-ivy/test/multi-project/projects/list/build.xml
new file mode 100644
index 0000000..3926148
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="list" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml
new file mode 100644
index 0000000..7fca587
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/ivy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="list"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="list" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <!--
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties
new file mode 100644
index 0000000..302cba7
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/sigil.properties
@@ -0,0 +1,14 @@
+
+# requirements
+
+-imports:\
+ version, \
+ org.apache.commons.cli
+
+# exports
+
+-bundles: list
+
+list;name: org.example.list
+list;-exports: list.*
+
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java b/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java
new file mode 100644
index 0000000..7467152
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/src/list/ListFile.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import version.Version;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class ListFile {
+ static {
+ Version.register("list");
+ }
+
+ public static Collection list(File dir) {
+ Collection files = new ArrayList();
+
+ return list(dir, files);
+ }
+
+ private static Collection list(File file, Collection files) {
+ if (file.isDirectory()) {
+ File[] f = file.listFiles();
+ for (int i=0; i<f.length; i++) {
+ list(f[i], files);
+ }
+ } else {
+ files.add(file);
+ }
+ return files;
+ }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java b/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java
new file mode 100644
index 0000000..0f8cb14
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/list/src/list/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package list;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName( "dir" )
+ .hasArg()
+ .withDescription( "list files in given dir" )
+ .create( "dir" );
+ Options options = new Options();
+
+ options.addOption(dir);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse( options, args );
+ File dir = new File(line.getOptionValue("dir", "."));
+ Collection files = ListFile.list(dir);
+ System.out.println("listing files in "+dir);
+ for (Iterator it = files.iterator(); it.hasNext(); ) {
+ System.out.println("\t"+it.next()+"\n");
+ }
+ } catch( ParseException exp ) {
+ // oops, something went wrong
+ System.err.println( "Parsing failed. Reason: " + exp.getMessage() );
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp( "list", options );
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties b/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties
new file mode 100644
index 0000000..0000617
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sigil-defaults.properties
@@ -0,0 +1,6 @@
+
+header;Bundle-Vendor: Paremus Limited
+
+package;org.osgi.framework: 1.7.1
+
+bad-key: wibble
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/build.properties b/sigil/bld-ivy/test/multi-project/projects/size/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/build.xml b/sigil/bld-ivy/test/multi-project/projects/size/build.xml
new file mode 100644
index 0000000..2a75fe5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="size" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml
new file mode 100644
index 0000000..33e9995
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/ivy.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="size"
+ status="integration"/>
+ <dependencies>
+ <!--
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->core" />
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties
new file mode 100644
index 0000000..cf239af
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/sigil.properties
@@ -0,0 +1,8 @@
+
+-bundles: size
+
+-exports: size
+
+-imports:\
+ list, \
+ version
diff --git a/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java b/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java
new file mode 100644
index 0000000..dcd4a1c
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/size/src/size/FileSize.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package size;
+
+import version.Version;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+public class FileSize {
+ static {
+ Version.register("size");
+ }
+
+ public static long totalSize(File dir) {
+ return totalSize(list.ListFile.list(dir));
+ }
+
+ public static long totalSize(Collection files) {
+ long total = 0;
+ for (Iterator it = files.iterator(); it.hasNext(); ) {
+ File f = (File)it.next();
+ total += f.length();
+ }
+ return total;
+ }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml
new file mode 100644
index 0000000..0b8ab4c
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="sizewhere" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml
new file mode 100644
index 0000000..6b86af0
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/ivy.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="sizewhere"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="sizewhere" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <!--
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="size" rev="latest.integration" conf="core->default" />
+ <dependency name="find" rev="latest.integration" conf="core" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ -->
+ </dependencies>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties
new file mode 100644
index 0000000..c272f9b
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/sigil.properties
@@ -0,0 +1,10 @@
+
+-bundles: sizewhere
+
+-exports: sizewhere
+
+-imports:\
+ find, \
+ size, \
+ version, \
+ org.apache.commons.cli
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java
new file mode 100644
index 0000000..131af66
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName( "dir" )
+ .hasArg()
+ .withDescription( "give total size of files in given dir" )
+ .create( "dir" );
+ Option name = OptionBuilder.withArgName( "name" )
+ .hasArg()
+ .withDescription( "give total size of files with given name" )
+ .create( "name" );
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse( options, args );
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ System.out.println("total size of files in "+dir+" containing "+name+": "+SizeWhere.totalSize(dir, name));
+ } catch( ParseException exp ) {
+ // oops, something went wrong
+ System.err.println( "Parsing failed. Reason: " + exp.getMessage() );
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp( "sizewhere", options );
+ }
+ }
+
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
new file mode 100644
index 0000000..3594bd9
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package sizewhere;
+
+import version.Version;
+import size.FileSize;
+import find.FindFile;
+
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public class SizeWhere {
+ static {
+ Version.register("sizewhere");
+ }
+
+ public static long totalSize(File dir, String name) {
+ return FileSize.totalSize(FindFile.find(dir, name));
+ }
+}
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/build.properties b/sigil/bld-ivy/test/multi-project/projects/version/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/build.xml b/sigil/bld-ivy/test/multi-project/projects/version/build.xml
new file mode 100644
index 0000000..375db20
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="version" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml b/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml
new file mode 100644
index 0000000..16e08f3
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/ivy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="version"
+ status="integration"/>
+</ivy-module>
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties b/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties
new file mode 100644
index 0000000..a940ffd
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/sigil.properties
@@ -0,0 +1,4 @@
+
+-bundles: version
+
+-exports: version
diff --git a/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java b/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java
new file mode 100644
index 0000000..0c69bc5
--- /dev/null
+++ b/sigil/bld-ivy/test/multi-project/projects/version/src/version/Version.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package version;
+
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+public class Version {
+ static {
+ versions = new HashMap();
+ register("version");
+ }
+
+ private static Map versions;
+
+ public static void register(String module) {
+ try {
+ InputStream moduleVersion = Version.class.getResourceAsStream("/"+module+".properties");
+ Properties props = new Properties();
+ props.load(moduleVersion);
+ String version = (String)props.get("version");
+ versions.put(module, version);
+ System.out.println("--- using "+module+" v"+version);
+ } catch (Exception ex) {
+ System.err.println("an error occured while registering "+module+": "+ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/sigil/bld-junit/.classpath b/sigil/bld-junit/.classpath
new file mode 100644
index 0000000..d97a6bc
--- /dev/null
+++ b/sigil/bld-junit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.cauldron.sigil.core.classpathContainer"/>
+ <classpathentry kind="output" path="build/classes"/>
+</classpath>
diff --git a/sigil/bld-junit/.project b/sigil/bld-junit/.project
new file mode 100644
index 0000000..455b5c3
--- /dev/null
+++ b/sigil/bld-junit/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>bld-junit</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.cauldron.sigil.core.newtonBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.cauldron.sigil.core.newtonnature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs b/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..51d8662
--- /dev/null
+++ b/sigil/bld-junit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Thu Feb 19 09:53:36 GMT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/bld-junit/build.xml b/sigil/bld-junit/build.xml
new file mode 100644
index 0000000..46ce9d1
--- /dev/null
+++ b/sigil/bld-junit/build.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<project name="bld-junit" default="bundle" basedir=".">
+ <import file="../bldcommon/common.xml"/>
+</project>
diff --git a/sigil/bld-junit/ivy.xml b/sigil/bld-junit/ivy.xml
new file mode 100644
index 0000000..9135f4a
--- /dev/null
+++ b/sigil/bld-junit/ivy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.cauldron"
+ module="bld.junit"
+ status="integration"/>
+ <publications>
+ <artifact name="org.cauldron.sigil.junit" />
+ </publications>
+</ivy-module>
diff --git a/sigil/bld-junit/sigil.properties b/sigil/bld-junit/sigil.properties
new file mode 100644
index 0000000..c22f88c
--- /dev/null
+++ b/sigil/bld-junit/sigil.properties
@@ -0,0 +1,22 @@
+
+# sigil project file, saved by plugin.
+
+-activator: org.cauldron.sigil.junit.activator.Activator
+
+version: 0.8.0
+
+-bundles: \
+ org.cauldron.sigil.junit, \
+
+-sourcedirs: \
+ src, \
+
+-exports: \
+ org.cauldron.sigil.junit.server, \
+
+-imports: \
+ junit.framework;version=4.5.0, \
+ org.osgi.framework;version=1.4.0, \
+ org.osgi.util.tracker;version=1.3.3, \
+
+# end
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java
new file mode 100644
index 0000000..0c84937
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/AbstractSigilTestCase.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit;
+
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import junit.framework.TestCase;
+
+public abstract class AbstractSigilTestCase extends TestCase {
+
+ private final static List<ServiceTracker> trackers = new LinkedList<ServiceTracker>();
+
+ private BundleContext ctx;
+
+ public void setBundleContext(BundleContext ctx) {
+ this.ctx = ctx;
+ }
+
+ protected BundleContext getBundleContext() {
+ return ctx;
+ }
+
+ @Override
+ protected void setUp() {
+ for ( Class<?> c : getReferences() ) {
+ ServiceTracker t = createBindTracker(c);
+ t.open();
+ trackers.add( t );
+ }
+ }
+
+ @Override
+ protected void tearDown() {
+ for ( ServiceTracker t : trackers ) {
+ t.close();
+ }
+ trackers.clear();
+ }
+
+
+ private ServiceTracker createBindTracker(final Class<?> c) {
+ return new ServiceTracker(ctx, c.getName(), new ServiceTrackerCustomizer() {
+ public Object addingService(ServiceReference reference) {
+ Object o = ctx.getService(reference);
+ Method m = getBindMethod(c);
+ if ( m != null ) invoke( m, o );
+ return o;
+ }
+
+ public void modifiedService(ServiceReference reference,
+ Object service) {
+ }
+
+ public void removedService(ServiceReference reference,
+ Object service) {
+ Method m = getUnbindMethod(c);
+ if ( m != null ) invoke( m, service );
+ ctx.ungetService(reference);
+ }
+ });
+ }
+
+ private void invoke(Method m, Object o) {
+ try {
+ m.invoke( this, new Object[] { o } );
+ } catch (Exception e) {
+ throw new IllegalStateException( "Failed to invoke binding method " + m, e);
+ }
+ }
+
+ protected abstract Class<?>[] getReferences();
+
+ protected abstract Method getBindMethod(Class<?> clazz);
+
+ protected abstract Method getUnbindMethod(Class<?> clazz);
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java
new file mode 100644
index 0000000..2614113
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/ReflectiveSigilTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+public abstract class ReflectiveSigilTestCase extends AbstractSigilTestCase {
+
+ private Class<?>[] references;
+ private Map<Class<?>, Method> bindMethods;
+ private Map<Class<?>, Method> unbindMethods;
+
+ @Override
+ protected Class<?>[] getReferences() {
+ introspect();
+ return references;
+ }
+
+ @Override
+ protected Method getBindMethod(Class<?> clazz) {
+ return bindMethods.get(clazz);
+ }
+
+ @Override
+ protected Method getUnbindMethod(Class<?> clazz) {
+ return unbindMethods.get(clazz);
+ }
+
+ private void introspect() {
+ if ( references == null ) {
+ bindMethods = findBindMethods(getClass(), "set", "add");
+ unbindMethods = findBindMethods(getClass(), "set", "remove");
+
+ HashSet<Class<?>> refs = new HashSet<Class<?>>();
+ refs.addAll( bindMethods.keySet() );
+ refs.addAll( unbindMethods.keySet() );
+ references = refs.toArray( new Class<?>[refs.size()] );
+ }
+ }
+
+ private static Map<Class<?>, Method> findBindMethods(Class<?> clazz, String... prefix) {
+ HashMap<Class<?>, Method> found = new HashMap<Class<?>, Method>();
+
+ checkDeclaredMethods(clazz, found, prefix);
+
+ return found;
+ }
+
+ private static void checkDeclaredMethods(Class<?> clazz, Map<Class<?>, Method> found, String...prefix) {
+ for ( Method m : clazz.getDeclaredMethods() ) {
+ if ( isMethodPrefixed(m, prefix) && isBindSignature(m) ) {
+ found.put( m.getParameterTypes()[0], m );
+ }
+ }
+
+ Class<?> sup = clazz.getSuperclass();
+ if ( sup != null && sup != Object.class ) {
+ checkDeclaredMethods(sup, found, prefix);
+ }
+
+ for ( Class<?> i : clazz.getInterfaces() ) {
+ checkDeclaredMethods(i, found, prefix);
+ }
+ }
+
+ private static boolean isMethodPrefixed(Method m, String...prefix) {
+ String n = m.getName();
+ for ( String p : prefix ) {
+ if ( n.startsWith( p ) && n.length() > p.length() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ private static boolean isBindSignature(Method m) {
+ return m.getReturnType() == Void.TYPE && m.getParameterTypes().length == 1;
+ }
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java
new file mode 100644
index 0000000..b22920b
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/activator/Activator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.activator;
+
+import org.cauldron.sigil.junit.server.JUnitService;
+import org.cauldron.sigil.junit.server.impl.JUnitServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * @author dave
+ */
+public class Activator implements BundleActivator {
+ private ServiceRegistration reg;
+ private JUnitServiceFactory service;
+
+ public void start(final BundleContext ctx) {
+ service = new JUnitServiceFactory();
+ service.start(ctx);
+ reg = ctx.registerService(JUnitService.class.getName(), service, null);
+ }
+
+ public void stop(BundleContext ctx) {
+ reg.unregister();
+ reg = null;
+ service.stop(ctx);
+ service = null;
+ }
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java
new file mode 100644
index 0000000..e111b26
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/JUnitService.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server;
+
+import java.util.Set;
+
+import org.osgi.framework.BundleContext;
+
+import junit.framework.TestSuite;
+
+public interface JUnitService {
+ Set<String> getTests();
+
+ TestSuite createTest(String test);
+
+ TestSuite createTest(String test, BundleContext ctx);
+}
\ No newline at end of file
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java
new file mode 100644
index 0000000..49fadf6
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.util.HashMap;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+
+public class JUnitServiceFactory implements ServiceFactory {
+
+ private HashMap<String, Class<? extends TestCase>> tests = new HashMap<String, Class<? extends TestCase>>();
+ private TestClassListener listener;
+
+ public void start(BundleContext ctx) {
+ listener = new TestClassListener(this);
+ ctx.addBundleListener(listener);
+ //listener.index(ctx.getBundle());
+ for ( Bundle b : ctx.getBundles() ) {
+ if ( b.getState() == Bundle.RESOLVED ) {
+ listener.index(b);
+ }
+ }
+ }
+
+ public void stop(BundleContext ctx) {
+ ctx.removeBundleListener(listener);
+ listener = null;
+ }
+
+ public Object getService(Bundle bundle, ServiceRegistration reg) {
+ return new JUnitServiceImpl(this, bundle.getBundleContext());
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration reg, Object service) {
+ }
+
+ public void registerTest(Class<? extends TestCase> clazz) {
+ tests.put( clazz.getName(), clazz );
+ }
+
+ public void unregister(Class<? extends TestCase> clazz) {
+ tests.remove(clazz.getName());
+ }
+
+ public Set<String> getTests() {
+ return new TreeSet<String>(tests.keySet());
+ }
+
+ public TestSuite getTest(String test) {
+ Class<? extends TestCase> tc = tests.get(test);
+ return tc == null ? null : new TestSuite(tc);
+ }
+
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java
new file mode 100644
index 0000000..c549cea
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/JUnitServiceImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.cauldron.sigil.junit.server.JUnitService;
+import org.osgi.framework.BundleContext;
+
+public class JUnitServiceImpl implements JUnitService {
+
+ private static final Logger log = Logger.getLogger(JUnitServiceImpl.class.getName());
+
+ private static final Class<?>[] BUNDLE_CONTEXT_PARAMS = new Class[] { BundleContext.class };
+
+ private final JUnitServiceFactory junitServiceFactory;
+ private final BundleContext bundleContext;
+
+ public JUnitServiceImpl(JUnitServiceFactory junitServiceFactory, BundleContext bundleContext) {
+ this.junitServiceFactory = junitServiceFactory;
+ this.bundleContext = bundleContext;
+ }
+
+ public Set<String> getTests() {
+ return junitServiceFactory.getTests();
+ }
+
+ public TestSuite createTest(String test) {
+ return createTest(test, null);
+ }
+
+ public TestSuite createTest(String test, BundleContext ctx) {
+ try {
+ TestSuite ts = junitServiceFactory.getTest(test);
+
+ if ( ts == null ) return null;
+
+ TestSuite ret = new TestSuite(ts.getName());
+
+ Enumeration<Test> e = ts.tests();
+
+ while ( e.hasMoreElements() ) {
+ Test t = e.nextElement();
+ setContext(t, ctx);
+ ret.addTest(t);
+ }
+
+ return ret;
+ }
+ catch (final NoClassDefFoundError e) {
+ TestSuite s = new TestSuite(test);
+ s.addTest( new Test() {
+ public int countTestCases() {
+ return 1;
+ }
+
+ public void run(TestResult result) {
+ result.addError(this, e);
+ }
+ });
+ return s;
+ }
+ catch (final RuntimeException e) {
+ TestSuite s = new TestSuite(test);
+ s.addTest( new Test() {
+ public int countTestCases() {
+ return 1;
+ }
+
+ public void run(TestResult result) {
+ result.addError(this, e);
+ }
+
+ });
+ return s;
+ }
+ }
+
+ private void setContext(Test t, BundleContext ctx) {
+ try {
+ Method m = findMethod( t.getClass(), "setBundleContext", BUNDLE_CONTEXT_PARAMS );
+ if ( m != null )
+ m.invoke(t, ctx == null ? bundleContext : ctx );
+ } catch (SecurityException e) {
+ log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+ } catch (IllegalArgumentException e) {
+ log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+ } catch (IllegalAccessException e) {
+ log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+ } catch (InvocationTargetException e) {
+ log.log( Level.WARNING, "Failed to set bundle context on " + t, e);
+ }
+ }
+
+ private Method findMethod(Class<?> clazz, String name,
+ Class<?>[] params) {
+ Method found = null;
+
+ for ( Method m : clazz.getDeclaredMethods() ) {
+ if ( m.getName().equals(name) && Arrays.deepEquals(m.getParameterTypes(), params) ) {
+ found = m;
+ break;
+ }
+ }
+
+ if ( found == null ) {
+ Class<?> c = clazz.getSuperclass();
+
+ if ( c != null && c != Object.class ) {
+ found = findMethod(c, name, params);
+ }
+ }
+
+ if ( found == null ) {
+ for ( Class<?> c : clazz.getInterfaces() ) {
+ found = findMethod(c, name, params);
+ if ( found != null ) {
+ break;
+ }
+ }
+ }
+
+ return found;
+ }
+}
diff --git a/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java
new file mode 100644
index 0000000..c3db3f9
--- /dev/null
+++ b/sigil/bld-junit/src/org/cauldron/sigil/junit/server/impl/TestClassListener.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.server.impl;
+
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+public class TestClassListener implements SynchronousBundleListener {
+ private static final Logger log = Logger.getLogger(TestClassListener.class.getName());
+
+ private final JUnitServiceFactory service;
+
+ private HashMap<Long, Class<TestCase>[]> registrations = new HashMap<Long, Class<TestCase>[]>();
+
+ public TestClassListener(JUnitServiceFactory service) {
+ this.service = service;
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ switch( event.getType() ) {
+ case BundleEvent.RESOLVED:
+ index( event.getBundle() );
+ break;
+ case BundleEvent.UNRESOLVED:
+ unindex( event.getBundle() );
+ break;
+ }
+ }
+
+ void index(Bundle bundle) {
+ if ( isTestBundle( bundle ) ) {
+ List<String> tests = findTests( bundle );
+
+ if ( !tests.isEmpty() ) {
+ LinkedList<Class<? extends TestCase>> regs = new LinkedList<Class<? extends TestCase>>();
+
+ for ( String jc : tests ) {
+ try {
+ Class<?> clazz = bundle.loadClass(jc);
+ if ( isTestCase(clazz) ) {
+ Class<? extends TestCase> tc = clazz.asSubclass(TestCase.class);
+ regs.add( tc );
+ service.registerTest(tc);
+ }
+ } catch (ClassNotFoundException e) {
+ log.log( Level.WARNING, "Failed to load class " + jc, e );
+ } catch (NoClassDefFoundError e) {
+ log.log( Level.WARNING, "Failed to load class " + jc, e );
+ }
+ }
+
+ registrations.put( bundle.getBundleId(), toArray(regs) );
+ }
+ }
+ }
+
+ private boolean isTestBundle(Bundle bundle) {
+ try {
+ bundle.loadClass(TestCase.class.getName());
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<TestCase>[] toArray(LinkedList<Class<? extends TestCase>> regs) {
+ return regs.toArray( new Class[regs.size()] );
+ }
+
+ private boolean isTestCase(Class<?> clazz) {
+ return
+ TestCase.class.isAssignableFrom(clazz) &&
+ !Modifier.isAbstract(clazz.getModifiers()) &&
+ !clazz.getPackage().getName().startsWith( "junit" );
+ }
+
+ void unindex(Bundle bundle) {
+ Class<TestCase>[] classes = registrations.remove(bundle.getBundleId());
+ if ( classes != null ) {
+ for ( Class<TestCase> tc : classes ) {
+ service.unregister(tc);
+ }
+ }
+ }
+
+ private List<String> findTests(Bundle bundle) {
+ @SuppressWarnings("unchecked") Enumeration<URL> urls = bundle.findEntries("", "*.class", true);
+
+ LinkedList<String> tests = new LinkedList<String>();
+ while( urls.hasMoreElements() ) {
+ URL url = urls.nextElement();
+ tests.add( toClassName( url ) );
+ }
+
+ return tests;
+ }
+
+ private String toClassName(URL url) {
+ String f = url.getFile();
+ String cn = f.substring(1, f.length() - 6 );
+ return cn.replace('/', '.');
+ }
+
+}
diff --git a/sigil/bldcommon/build.properties b/sigil/bldcommon/build.properties
new file mode 100644
index 0000000..ad4583b
--- /dev/null
+++ b/sigil/bldcommon/build.properties
@@ -0,0 +1,40 @@
+# common properties
+# easier to set here than in xml file
+
+# set common.dir when used without ant (e.g. IvyDE)
+common.dir = ${ivy.settings.dir}
+
+ivy.jar = ${common.dir}/../bld-ivy/lib/compile/ivy-2.0.0-rc1.jar
+sigil-ivy-plugin.jar = ${common.dir}/../bld-ivy/target/sigil-ivy-plugin.jar
+
+build.dir = ${basedir}/build
+build_xml = build.xml
+classes.dir = ${build.dir}/classes
+deps.dir = ${build.dir}/deps
+composite.dir = ${basedir}/xml
+src.dir = ${basedir}/src
+ivy.file = ${basedir}/ivy.xml
+
+_build.dir = ${build.dir}
+build.lib.dir = ${_build.dir}/lib
+build.etc.dir = ${_build.dir}/etc
+
+top-build.dir = ${common.dir}/../build
+dist.dir = ${top-build.dir}/dist
+install.dir = ${top-build.dir}/install
+cache.dir = ${top-build.dir}/ivy-cache
+repository.dir = ${top-build.dir}/repository
+# note: clean-local task assumes repository.pattern starts with ${ivy.module}
+repository.pattern = [module]/[revision]/[type]s/[artifact].[ext]
+
+community.version = 1.4.0.SNAPSHOT
+jini.version = 2.1
+paremus.version = ${default.version}
+bundle.version = ${paremus.version}
+resolve.log = download-only
+
+# over-ridden by hudson
+buildVersion = 0.8.0-dev
+
+# end
+
diff --git a/sigil/bldcommon/common.xml b/sigil/bldcommon/common.xml
new file mode 100644
index 0000000..957854c
--- /dev/null
+++ b/sigil/bldcommon/common.xml
@@ -0,0 +1,466 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <dirname property="common.dir" file="${ant.file.common}"/>
+ <property file="${common.dir}/sigil-defaults.properties" prefix="default."/>
+ <property file="${common.dir}/build.properties"/>
+
+ <!-- =====================================================
+ stuff intended to be over-ridden is prefixed with my.
+ =====================================================-->
+
+ <fileset id="my.projects" dir="${basedir}">
+ <include name="**/${build_xml}"/>
+ </fileset>
+
+ <path id="my.classpath">
+ <fileset dir="${basedir}" includes="lib/*.jar"/>
+ </path>
+
+ <path id="javac.classpath">
+ <path refid="my.classpath"/>
+ <fileset dir="${deps.dir}" />
+ </path>
+
+ <path id="sigil.classpath">
+ <path refid="javac.classpath" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <!-- =================================
+ target: load-ivy
+ ================================= -->
+ <target name="load-ivy" depends="ident,ivy-taskdefs">
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ </target>
+
+ <!-- =================================
+ target: ivy-taskdefs
+ ================================= -->
+ <target name="ivy-taskdefs" unless="ivy.loaded">
+ <property name="ivy.loaded" value="true"/>
+ <echo message="Loading Ivy ... common.dir=${common.dir}"/>
+
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant"
+ classpath="${ivy.jar}:${sigil-ivy-plugin.jar}"/>
+
+ <taskdef name="sigil.bundle"
+ classname="org.cauldron.bld.ant.BundleTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+
+ <taskdef name="sigil.bundle.info"
+ classname="org.cauldron.bld.ant.BundleInfoTask"
+ classpath="${sigil-ivy-plugin.jar}"/>
+ </target>
+
+
+ <!-- =================================
+ target: build (default target)
+ ================================= -->
+ <target name="build" depends="publish-local, composites" />
+
+ <target name="ident">
+ <echo message="${ant.project.name}"/>
+ </target>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="load-ivy"
+ description="--> resolve and retrieve dependencies with ivy">
+ <mkdir dir="${deps.dir}"/>
+ <ivy:resolve file="${ivy.file}" log="${resolve.log}"/>
+ <ivy:retrieve pattern="${deps.dir}/[artifact].[ext]"
+ symlink="true" sync="true"/>
+ <!-- sync=true removes empty deps dir, so re-create it -->
+ <mkdir dir="${deps.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: publish-local
+ ================================= -->
+
+ <target name="publish-check" unless="bundle.modified">
+ <ivy:info file="${ivy.file}"/>
+ <condition property="bundle.modified">
+ <not>
+ <available file="${repository.dir}/local/${ivy.module}" type="dir"/>
+ </not>
+ </condition>
+ </target>
+
+ <target name="publish-local" depends="bundle, publish-check" if="bundle.modified"
+ description="--> publish project in the local repository">
+
+ <tstamp>
+ <format property="now" pattern="yyyyMMddHHmmss"/>
+ </tstamp>
+ <property name="local-version" value="${now}"/>
+
+ <antcall target="clean-local"/>
+ <ivy:publish artifactspattern="${build.lib.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${local-version}"
+ pubdate="${now}"
+ forcedeliver="true"
+ status="integration"/>
+ <echo message="project ${ant.project.name} published locally with version ${local-version}" />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve"
+ description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve"
+ description="--> compile the project">
+ <!-- uncomment the following to debug classpath -->
+ <!--pathconvert property="cp" refid="javac.classpath"/>
+ <echo>Classpath - ${cp}</echo-->
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}"
+ destdir="${classes.dir}"
+ classpathref="javac.classpath"
+ target="1.5"
+ debug="true" />
+ </target>
+
+ <!-- =================================
+ target: bundle
+ ================================= -->
+ <target name="bundle" depends="compile"
+ description="--> build OSGi bundle(s) for this project">
+ <mkdir dir="${build.lib.dir}"/>
+ <sigil.bundle
+ classpathref="sigil.classpath"
+ destpattern="${build.lib.dir}/[name].[ext]"
+ force="${bundle.force}"
+ property="bundle.modified" />
+ </target>
+
+ <!-- =================================
+ target: composites
+ ================================= -->
+ <available file="${composite.dir}" type="dir"
+ property="composite.dir.present"/>
+
+ <target name="composites" if="composite.dir.present"
+ description="--> filter xml composites replacing ${VERSION} etc.">
+ <mkdir dir="${build.etc.dir}"/>
+ <copy todir="${build.etc.dir}">
+ <fileset dir="${composite.dir}">
+ <exclude name="*-template.composite"/>
+ <include name="*.composite"/>
+ <include name="*.system"/>
+ </fileset>
+ <filterset begintoken="@" endtoken="@">
+ <filter token="VERSION" value="${bundle.version}"/>
+ <filter token="COMMUNITY_VERSION" value="${community.version}"/>
+ <filter token="JINI_VERSION" value="${jini.version}"/>
+ <filter token="PAREMUS_VERSION" value="${paremus.version}"/>
+ </filterset>
+ </copy>
+ </target>
+
+ <!-- =================================
+ target: install
+ install-bin, install-etc deprecated in favour of single
+ assemble/bin, assemble/etc dirs
+ ================================= -->
+ <target name="install"
+ depends="install-lib, install-composites"/>
+
+ <available file="bin" type="dir"
+ property="bin.dir.present"/>
+ <available file="etc" type="dir"
+ property="etc.dir.present"/>
+
+ <target name="install-bin" if="bin.dir.present">
+ <mkdir dir="${install.dir}/bin"/>
+ <copy todir="${install.dir}/bin">
+ <fileset dir="bin" />
+ </copy>
+ <chmod dir="${install.dir}/bin" perm="755" excludes="*.bat"/>
+ </target>
+
+ <target name="install-etc" if="etc.dir.present">
+ <mkdir dir="${install.dir}/etc"/>
+ <copy todir="${install.dir}/etc">
+ <fileset dir="etc" />
+ </copy>
+ </target>
+
+ <target name="composites-available">
+ <available file="${build.etc.dir}" type="dir"
+ property="build.etc.dir.present"/>
+ </target>
+
+ <target name="install-composites"
+ depends="composites, composites-available"
+ if="build.etc.dir.present">
+ <copy todir="${install.dir}/etc">
+ <fileset dir="${build.etc.dir}" />
+ </copy>
+ </target>
+
+ <target name="libs-available">
+ <available file="${build.lib.dir}" type="dir"
+ property="build.lib.dir.present"/>
+ </target>
+
+ <target name="install-lib"
+ depends="publish-local, libs-available"
+ if="build.lib.dir.present">
+ <copy todir="${install.dir}/lib">
+ <fileset dir="${build.lib.dir}" />
+ <mapper type="glob" from="*.jar" to="*-${buildVersion}.jar"/>
+ </copy>
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local" depends="load-ivy"
+ description="--> cleans the local repository for the current module">
+ <ivy:info file="${ivy.file}"/>
+ <delete dir="${repository.dir}/local/${ivy.module}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-deps
+ ================================= -->
+ <target name="clean-deps"
+ description="--> clean the project dependencies directory">
+ <delete includeemptydirs="true" dir="${deps.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-build
+ ================================= -->
+ <target name="clean-build"
+ description="--> clean the project built files">
+ <delete includeemptydirs="true" dir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" depends="ident, clean-build, clean-deps"
+ description="--> clean the project" />
+
+
+ <!-- =================================
+ target: buildlist
+ ================================= -->
+ <target name="buildlist" depends="load-ivy">
+ <ivy:buildlist reference="ordered-list"
+ onMissingDescriptor="skip">
+ <fileset refid="my.projects"/>
+ </ivy:buildlist>
+ </target>
+
+ <!-- =================================
+ target: build-list
+ ================================= -->
+ <target name="build-list" depends="buildlist"
+ description="--> build all projects in the right order">
+ <property name="target" value="build"/>
+ <subant target="${target}" buildpathref="ordered-list">
+ <propertyset>
+ <propertyref name="ivy.loaded" />
+ </propertyset>
+ </subant>
+ </target>
+
+ <!-- =================================
+ target: install-list
+ ================================= -->
+ <target name="install-list"
+ description="--> install all projects">
+ <antcall target="build-list">
+ <param name="target" value="install"/>
+ </antcall>
+ </target>
+
+ <!-- =================================
+ target: ident-list
+ ================================= -->
+ <target name="ident-list"
+ description="--> identify projects">
+ <antcall target="build-list">
+ <param name="target" value="ident"/>
+ </antcall>
+ </target>
+
+ <!-- =================================
+ target: clean-list
+ ================================= -->
+ <target name="clean-list"
+ description="--> clean all projects">
+ <antcall target="build-list">
+ <param name="target" value="clean"/>
+ </antcall>
+ </target>
+
+ <!-- =================================
+ target: clean-all
+ ================================= -->
+ <target name="clean-all" depends="clean-list, clean"
+ description="--> clean repository, cache, and all projects">
+ <delete dir="${repository.dir}"/>
+ <delete dir="${cache.dir}"/>
+ <delete dir="${install.dir}"/>
+ <delete dir="${dist.dir}"/>
+ </target>
+
+ <target name="sigil.test" depends="ivy-taskdefs">
+ <mkdir dir="${dist.dir}/lib/sigil" />
+ <copy todir="${dist.dir}/lib/sigil" overwrite="true">
+ <fileset dir="${common.dir}/test/lib">
+ <include name="*.jar" />
+ </fileset>
+ </copy>
+
+ <mkdir dir="${dist.dir}/etc/sigil/boot" />
+
+ <copy todir="${dist.dir}/etc/sigil/boot" overwrite="true">
+ <fileset dir="${common.dir}/test/etc/boot">
+ <include name="*" />
+ </fileset>
+ </copy>
+
+ <property name="install.script" value="${dist.dir}/etc/sigil/boot/2-install-tests" />
+ <delete file="${install.script}" />
+
+ <for param="project">
+ <fileset refid="my.projects" />
+ <sequential>
+ <antcall target="sigil.test.setup">
+ <param name="project" value="@{project}"/>
+ </antcall>
+ </sequential>
+ </for>
+
+ <for param="project">
+ <fileset refid="my.projects" />
+ <sequential>
+ <antcall target="sigil.test.install">
+ <param name="project" value="@{project}"/>
+ </antcall>
+ </sequential>
+ </for>
+
+ <delete dir="${dist.dir}/../test-results" />
+
+ <exec executable="sh" osfamily="unix">
+ <arg line="${dist.dir}/bin/container equinox -verbose=true" />
+ <arg line="-bootScript=etc/sigil/boot" />
+ </exec>
+ </target>
+
+ <target name="sigil.test.setup">
+ <property name="dir" location="${project}/../" />
+ <path id="test.resources">
+ <fileset dir="${dir}">
+ <include name="build/lib/*.jar" />
+ <exclude name="**/*-dl.jar" />
+ </fileset>
+ <fileset dir="${dir}">
+ <include name="build/deps/*.jar" />
+ <exclude name="**/*!*.jar" />
+ </fileset>
+ </path>
+ <for param="jar">
+ <path refid="test.resources"/>
+ <sequential>
+ <antcall target="sigil.test.load">
+ <param name="jar" value="@{jar}" />
+ </antcall>
+ </sequential>
+ </for>
+ </target>
+
+ <target name="sigil.test.install">
+ <property name="dir" location="${project}/../" />
+ <path id="test.bundles">
+ <fileset dir="${dir}">
+ <include name="build/lib/*test.jar" />
+ </fileset>
+ </path>
+ <for param="jar">
+ <path refid="test.bundles"/>
+ <sequential>
+ <antcall target="sigil.test.start">
+ <param name="jar" value="@{jar}" />
+ </antcall>
+ </sequential>
+ </for>
+ </target>
+
+ <target name="sigil.test.load">
+ <sigil.bundle.info bundle="${jar}" header="Bundle-SymbolicName" property="test.bundle.symbolic.name"/>
+ <if>
+ <isset property="test.bundle.symbolic.name"/>
+ <then>
+ <echo file="${install.script}" append="true">cds load boot ${test.bundle.symbolic.name}.jar ${jar}
+</echo>
+ </then>
+ </if>
+ </target>
+
+ <target name="sigil.test.start">
+ <sigil.bundle.info bundle="${jar}" header="Bundle-SymbolicName" property="test.bundle.symbolic.name"/>
+ <if>
+ <isset property="test.bundle.symbolic.name"/>
+ <then>
+ <sigil.bundle.info bundle="${jar}" header="Bundle-Version" property="test.bundle.version" defaultvalue="0"/>
+ <sigil.bundle.info bundle="${jar}" header="Fragment-Host" property="test.bundle.fragment"/>
+ <if>
+ <isset property="test.bundle.fragment" />
+ <then>
+ <propertyregex property="test.bundle.fragment.name"
+ input="${test.bundle.fragment}"
+ regexp="([^;]*).*"
+ select="\1"
+ casesensitive="false" />
+
+ <echo file="${install.script}" append="true">nim policy -a osgi.installed.bundle/${test.bundle.fragment.name} osgi.fragment.bundle/${test.bundle.symbolic.name}:${test.bundle.version}
+</echo>
+ <echo file="${install.script}" append="true">nim add ${test.bundle.symbolic.name}:${test.bundle.version}@fragment
+</echo>
+ </then>
+ <else>
+ <echo file="${install.script}" append="true">nim add ${test.bundle.symbolic.name}:${test.bundle.version}@installed
+</echo>
+ </else>
+ </if>
+ </then>
+ </if>
+ </target>
+</project>
diff --git a/sigil/bldcommon/ivysettings.xml b/sigil/bldcommon/ivysettings.xml
new file mode 100644
index 0000000..2ce1415
--- /dev/null
+++ b/sigil/bldcommon/ivysettings.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/build.properties"/>
+ <caches defaultCacheDir="${cache.dir}" />
+
+ <settings defaultResolver="local" circularDependencyStrategy="error" />
+
+ <!--
+ <classpath file="${sigil-ivy-plugin.jar}" />
+ Ant tasks "subant" and "ant" cause IvySettings to be re-loaded,
+ which then re-loads SigilParser in a new URLClassLoader,
+ which causes ProjectRepository to be re-initialised. Outch!
+ Loading sigil-ivy-plugin.jar in load-ivy task, uses the same ClassLoader
+ and thus avoids re-initialising ProjectRepositiory.
+ Search for Derek Baum in the October 2008 ant-dev archives for my attempt
+ at getting the Ivy communiuty to fix this.
+ http://mail-archives.apache.org/mod_mbox/ant-dev/200810.mbox/browser
+ -->
+ <typedef name="sigil-parser" classname="org.cauldron.bld.ivy.SigilParser" />
+ <typedef name="sigil" classname="org.cauldron.bld.ivy.SigilResolver" />
+
+ <parsers>
+ <sigil-parser config="${ivy.settings.dir}/sigil-repos.properties"
+ quiet="true"/>
+ </parsers>
+
+ <resolvers>
+ <sigil name="sigil"
+ config="${ivy.settings.dir}/sigil-repos.properties"
+ extractBCP="true"/>
+
+ <filesystem name="local">
+ <ivy pattern="${repository.dir}/local/${repository.pattern}" />
+ <artifact pattern="${repository.dir}/local/${repository.pattern}" />
+ </filesystem>
+
+ <filesystem name="shared">
+ <ivy pattern="${repository.dir}/shared/${repository.pattern}" />
+ <artifact pattern="${repository.dir}/shared/${repository.pattern}" />
+ </filesystem>
+ </resolvers>
+
+ <modules>
+ <module organisation="sigil" resolver="sigil"/>
+ </modules>
+</ivysettings>
diff --git a/sigil/bldcommon/sigil-repos.properties b/sigil/bldcommon/sigil-repos.properties
new file mode 100644
index 0000000..ace83cc
--- /dev/null
+++ b/sigil/bldcommon/sigil-repos.properties
@@ -0,0 +1,24 @@
+# sigil repository config
+
+# repository config
+
+-repositories: system, project, bld-common
+
+system;provider: system
+system;level: -1
+
+project;provider: project
+project;level: 0
+project;pattern: ${..}/**/[sigilproject]
+
+bld-common;provider: filesystem
+bld-common;level: 1
+bld-common;recurse: true
+bld-common;dir: ${.}/lib
+
+spring;provider: obr
+spring;level: 2
+spring;url: http://sigil.codecauldron.org/spring-repository.obr
+spring;index: ../build/spring-repository.obr
+
+# end
diff --git a/sigil/build.xml b/sigil/build.xml
new file mode 100644
index 0000000..04f030e
--- /dev/null
+++ b/sigil/build.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="Sigil" default="build">
+
+ <target name="build-eclipse">
+ <subant buildpath="sigil-builder" target="build"/>
+ </target>
+
+ <target name="build-ivy">
+ <subant buildpath="bld-ivy" target="dist"/>
+ </target>
+
+ <target name="build-junit">
+ <subant buildpath="bld-junit" target="install"/>
+ <subant buildpath="bld-junit-cli" target="install"/>
+ </target>
+
+ <target name="clean">
+ <delete dir="build" />
+ <subant buildpath="sigil-builder" target="clean"/>
+ <subant buildpath="bld-ivy" target="clean"/>
+ <subant buildpath="bld-junit" target="clean"/>
+ <subant buildpath="bld-junit-cli" target="clean"/>
+ </target>
+
+ <target name="build" depends="build-eclipse, build-ivy, build-junit" />
+
+ <target name="ci-commit" depends="clean, build"/>
+
+ <target name="ci-daily" depends="clean, build">
+ <subant buildpath="sigil-builder" target="new.updateSite"/>
+ </target>
+
+</project>
diff --git a/sigil/misc/do_notice b/sigil/misc/do_notice
new file mode 100644
index 0000000..933e145
--- /dev/null
+++ b/sigil/misc/do_notice
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+here=$(dirname $0)
+
+find . \
+ ! \( -type d \( -name .svn -o -name build \) -prune \) \
+ ! -name '*.jar' ! -name .classpath ! -name .project \
+ ! -name '*.gif' ! -name '*.ico' ! -name '*.png' \
+ ! -name '*.config' ! -name '*.policy' \
+ ! -name '*.swp' ! -name '*.txt' ! -name '*.properties' \
+ -type f |
+xargs $here/notice.pl $*
+
diff --git a/sigil/misc/notice.pl b/sigil/misc/notice.pl
new file mode 100755
index 0000000..64b40e9
--- /dev/null
+++ b/sigil/misc/notice.pl
@@ -0,0 +1,295 @@
+#!/usr/bin/env perl
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+use FileHandle;
+
+my $dryrun = 0;
+my $quiet = 0;
+
+#
+# Usage: notice.pl [-n] [-q] files...
+#
+
+my $NOTICE = <<'EOT';
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+EOT
+
+# Note: source code notices must not be changed by code formatter
+
+my $NOTICE_java = $NOTICE;
+$NOTICE_java =~ s/^/ * /gm;
+$NOTICE_java = "/*\n" . $NOTICE_java . " */\n\n";
+
+my $NOTICE_xml = $NOTICE;
+$NOTICE_xml =~ s/^/ /gm;
+$NOTICE_xml = "<!--\n" . $NOTICE_xml . "-->\n";
+
+my $NOTICE_sh = $NOTICE;
+$NOTICE_sh =~ s/^/# /gm;
+
+my $NOTICE_bat = $NOTICE;
+$NOTICE_bat =~ s/^/:: /gm;
+
+sub addNotice {
+ my ($fh, $file, $notice) = @_;
+ print "$file\n";
+
+ return if ($dryrun);
+
+ my $tmp = "$file.tmp";
+ my $fh2 = new FileHandle(">$tmp");
+
+ print $fh2 $notice;
+ while (<$fh>) {
+ print $fh2 $_;
+ }
+
+ unlink($file);
+ rename($tmp, $file);
+}
+
+#
+# locate existing copyright & license
+# starts at top of file
+# ends at "package" statement
+#
+sub notice_java {
+ my $file = shift;
+ my $fh = new FileHandle($file);
+ my $header;
+ my $package = undef;
+
+ while (<$fh>) {
+ if (/^package\s+[\w.]+\s*;/) {
+ $package = $_;
+ last;
+ }
+ last if ($. > 200);
+ $header .= $_;
+ }
+
+ if (! $package) {
+ print STDERR "$file: ERROR: no package statement.\n";
+ return;
+ }
+
+ return if ($header eq $NOTICE_java);
+
+ # completely replace all before package statement
+ # so DON'T change below, without changing above
+ # to stop at end-of-first-comment.
+ $header = '';
+ #$header = '' if ($header =~ /copyright/im);
+ #$header = "\n" . $header unless ($header =~ /^\n/);
+
+ addNotice($fh, $file, $NOTICE_java . $header . $package);
+}
+
+sub notice_xml {
+ my $file = shift;
+ my $fh = new FileHandle($file);
+ my $header = undef;
+ my $decl = qq(<?xml version="1.0"?>\n);
+ my $end = 0;
+ my $start = 0;
+
+ while (<$fh>) {
+ if ($. == 1 && /^\<\?/) {
+ $decl = $_;
+ next;
+ }
+
+ $header .= $_;
+
+ if (!$start) {
+ last unless (/^<!--/);
+ $start = 1;
+ }
+
+ if (/-->/) {
+ $end = 1;
+ last;
+ }
+ }
+
+ return if ($header eq $NOTICE_xml);
+
+ $header = '' if ($header =~ /copyright/im);
+
+ if ($start && !$end) {
+ print STDERR "$file: ERROR: initial comment not terminated.\n";
+ return;
+ }
+
+ addNotice($fh, $file, $decl . $NOTICE_xml . $header);
+}
+
+sub notice_sh {
+ my $file = shift;
+ my $fh = new FileHandle($file);
+ my $header = undef;
+ my $hashbang = undef;
+ my $end = '';
+ my $start = '';
+
+ while (<$fh>) {
+ if ($. == 1 && /^#!/) {
+ $hashbang = $_;
+ next;
+ }
+
+ if (!$start) {
+ unless (/^(#|\s*$)/) {
+ $header = $_;
+ last;
+ }
+ $start = 1;
+ next if (/^\s*$/);
+ }
+
+ if (!/^#/) {
+ $end = $_;
+ last;
+ }
+
+ $header .= $_;
+ }
+
+ return if ($header eq $NOTICE_sh);
+
+ $hashbang .= "\n" if ($hashbang);
+
+ $header = '' if ($header =~ /copyright/im);
+ $header .= $end;
+ $header = "\n" . $header unless ($header =~ /^\n/);
+
+ if ($start && !$end) {
+ print STDERR "$file: ERROR: initial comment not terminated.\n";
+ return;
+ }
+
+ addNotice($fh, $file, $hashbang . $NOTICE_sh . $header);
+}
+
+sub notice_bat {
+ my $file = shift;
+ my $fh = new FileHandle($file);
+ my $header = undef;
+ my $atecho = undef;
+ my $end = '';
+ my $start = '';
+
+ while (<$fh>) {
+ s/\r\n$/\n/;
+ if ($. == 1 && /^\@echo/) {
+ $atecho = $_;
+ next;
+ }
+
+ if (!$start) {
+ unless (/^(::|\s*$)/) {
+ $header = $_;
+ last;
+ }
+ $start = 1;
+ next if (/^\s*$/);
+ }
+
+ if (!/^::/) {
+ $end = $_;
+ last;
+ }
+
+ $header .= $_;
+ }
+
+ return if ($header eq $NOTICE_bat);
+
+ $atecho .= "\n" if ($atecho);
+
+ $header = '' if ($header =~ /copyright/im);
+ $header .= $end;
+ $header = "\n" . $header unless ($header =~ /^\n/);
+
+ if ($start && !$end) {
+ print STDERR "$file: ERROR: initial comment not terminated.\n";
+ return;
+ }
+
+ $header = $atecho . $NOTICE_bat . $header;
+ $header =~ s/\n/\r\n/mg;
+ addNotice($fh, $file, $header);
+}
+
+sub notice_unknown {
+ my $file = shift;
+ my $fh = new FileHandle($file);
+
+ my $line = <$fh>;
+
+ if ($line =~ /^#!/) {
+ notice_sh($file);
+ }
+ elsif ($line =~ /^\<\?xml/) {
+ notice_xml($file);
+ }
+ elsif (! $quiet) {
+ print STDERR "$file: unknown file type.\n";
+ }
+}
+
+foreach my $f (@ARGV) {
+ if ($f eq '-n') {
+ $dryrun++;
+ }
+ elsif ($f eq '-q') {
+ $quiet++;
+ }
+ elsif ($f =~ /\.java$/) {
+ notice_java($f);
+ }
+ elsif ($f =~ /\.(xml|xsd|system|composite)$/) {
+ notice_xml($f);
+ }
+ elsif ($f =~ /\.(sh|app|ini|xargs)$/) {
+ notice_sh($f);
+ }
+ elsif ($f =~ /\.(bat)$/) {
+ notice_bat($f);
+ }
+ else {
+ notice_unknown($f);
+ }
+}
+
diff --git a/sigil/org.cauldron.bld.core.tests/.classpath b/sigil/org.cauldron.bld.core.tests/.classpath
new file mode 100644
index 0000000..466e08f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.cauldron.bld.core"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.core.tests/.project b/sigil/org.cauldron.bld.core.tests/.project
new file mode 100644
index 0000000..44f71df
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.bld.core.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java
new file mode 100644
index 0000000..b4bfc51
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/BundleModelElementTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.util.Arrays;
+
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.common.VersionRange;
+
+import junit.framework.TestCase;
+
+public class BundleModelElementTest extends TestCase {
+
+ public BundleModelElementTest(String name) {
+ super(name);
+ }
+
+ public void testAddRequires() {
+ BundleModelElement element = new BundleModelElement();
+ checkRequires(element);
+ }
+
+ public void testAddImports() {
+ BundleModelElement element = new BundleModelElement();
+ checkImports(element);
+ }
+
+ public void testAddImportsAndRequires() {
+ BundleModelElement element = new BundleModelElement();
+ checkImports(element);
+ checkRequires(element);
+
+ element = new BundleModelElement();
+ checkRequires(element);
+ checkImports(element);
+ }
+
+ private void checkImports(BundleModelElement element) {
+ PackageImport foo = new PackageImport();
+ foo.setPackageName("foo");
+ foo.setVersions( VersionRange.parseVersionRange("1.0.0") );
+ PackageImport bar = new PackageImport();
+ bar.setPackageName( "bar" );
+ bar.setVersions( VersionRange.parseVersionRange("[2.2.2, 3.3.3]") );
+ PackageImport baz = new PackageImport();
+ baz.setPackageName( "baz" );
+ baz.setVersions( VersionRange.parseVersionRange("[3.0.0, 4.0.0)") );
+
+ element.addChild(foo.clone());
+ element.addChild(bar.clone());
+ element.addChild(baz.clone());
+
+ assertTrue( Arrays.asList(element.children()).contains(foo) );
+ assertTrue( Arrays.asList(element.children()).contains(bar) );
+ assertTrue( Arrays.asList(element.children()).contains(baz) );
+ }
+
+ private void checkRequires(BundleModelElement element) {
+ RequiredBundle foo = new RequiredBundle();
+ foo.setSymbolicName( "foo" );
+ foo.setVersions( VersionRange.parseVersionRange("1.0.0") );
+ RequiredBundle bar = new RequiredBundle();
+ bar.setSymbolicName( "bar" );
+ bar.setVersions( VersionRange.parseVersionRange("[2.2.2, 3.3.3]") );
+ RequiredBundle baz = new RequiredBundle();
+ baz.setSymbolicName( "baz" );
+ baz.setVersions( VersionRange.parseVersionRange("[3.0.0, 4.0.0)") );
+
+ element.addChild(foo.clone());
+ element.addChild(bar.clone());
+ element.addChild(baz.clone());
+
+ assertTrue( Arrays.asList(element.children()).contains(foo) );
+ assertTrue( Arrays.asList(element.children()).contains(bar) );
+ assertTrue( Arrays.asList(element.children()).contains(baz) );
+ }
+}
diff --git a/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java
new file mode 100644
index 0000000..080d5ab
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/src/org/cauldron/bld/core/ConfigTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class ConfigTest extends TestCase {
+
+ static final URI base = URI.create("test/ConfigTest/sigil.properties");
+
+ public ConfigTest(String name) {
+ super(name);
+ }
+
+ public void testSimple() throws IOException {
+ IBldProject project = BldFactory.getProject(base.resolve("test1.properties"));
+
+ ISigilBundle bundle = project.getDefaultBundle();
+ IBundleModelElement info = bundle.getBundleInfo();
+
+ checkImports(info.getImports());
+
+ //IBundleModelElement requirements = project.getRequirements();
+ }
+
+ private void checkImports(Set<IPackageImport> imports) {
+ PackageImport foo = new PackageImport();
+ foo.setPackageName("foo");
+ foo.setVersions(VersionRange.parseVersionRange("1.0.0"));
+ PackageImport bar = new PackageImport();
+ bar.setPackageName( "bar" );
+ bar.setVersions(VersionRange.parseVersionRange("[2.2.2, 3.3.3]"));
+ PackageImport baz = new PackageImport();
+ baz.setPackageName( "baz" );
+ baz.setVersions(VersionRange.parseVersionRange("[3.0.0, 4.0.0)"));
+
+ assertTrue(foo.toString(), imports.contains(foo));
+ assertTrue(bar.toString(), imports.contains(bar));
+ assertTrue(baz.toString(), imports.contains(baz));
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties b/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties
new file mode 100644
index 0000000..043837f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core.tests/test/ConfigTest/test1.properties
@@ -0,0 +1,10 @@
+
+# test1
+
+-bundles: default
+
+-imports: \
+ foo;version=1.0, \
+ bar;version="[2.2.2,3.3.3]", \
+ baz;version="[3.0.0,4.0.0)", \
+
diff --git a/sigil/org.cauldron.bld.core/.classpath b/sigil/org.cauldron.bld.core/.classpath
new file mode 100644
index 0000000..b26935e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="lib/bndlib.jar" sourcepath="/bld-ivy/lib/ant/bnd-0.0.312.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.core/.project b/sigil/org.cauldron.bld.core/.project
new file mode 100644
index 0000000..3bbec56
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.bld.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF b/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..cc6768b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/META-INF/MANIFEST.MF
@@ -0,0 +1,22 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil_bld Plug-in
+Bundle-SymbolicName: org.cauldron.bld.core;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Activator: org.cauldron.bld.core.BldCore
+Bundle-ActivationPolicy: lazy
+Export-Package: org.cauldron.bld.bnd,
+ org.cauldron.bld.config,
+ org.cauldron.bld.core,
+ org.cauldron.bld.core.licence,
+ org.cauldron.bld.core.repository,
+ org.cauldron.sigil.model,
+ org.cauldron.sigil.model.common,
+ org.cauldron.sigil.model.eclipse,
+ org.cauldron.sigil.model.osgi,
+ org.cauldron.sigil.repository
+Import-Package: org.osgi.framework
+Require-Bundle: org.eclipse.equinox.common;bundle-version="3.4.0"
+Bundle-ClassPath: lib/bndlib.jar,
+ .
diff --git a/sigil/org.cauldron.bld.core/build.properties b/sigil/org.cauldron.bld.core/build.properties
new file mode 100644
index 0000000..66f45cb
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ profiles/,\
+ lib/bndlib.jar
diff --git a/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile b/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile
new file mode 100644
index 0000000..1157a29
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/CDC-1.0_Foundation-1.0.profile
@@ -0,0 +1,19 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.microedition.io
+org.osgi.framework.bootdelegation = \
+ javax.microedition.io
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ CDC-1.0/Foundation-1.0
+osgi.java.profile.name = CDC-1.0/Foundation-1.0
diff --git a/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile b/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile
new file mode 100644
index 0000000..374300a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/CDC-1.1_Foundation-1.1.profile
@@ -0,0 +1,24 @@
+###############################################################################
+# Copyright (c) 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.microedition.io,\
+ javax.microedition.pki,\
+ javax.security.auth.x500
+org.osgi.framework.bootdelegation = \
+ javax.microedition.io,\
+ javax.microedition.pki,\
+ javax.security.auth.x500
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ CDC-1.0/Foundation-1.0,\
+ CDC-1.1/Foundation-1.1
+osgi.java.profile.name = CDC-1.1/Foundation-1.1
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile
new file mode 100644
index 0000000..28f973a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.2.profile
@@ -0,0 +1,42 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ org.omg.CORBA,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextPackage
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ JRE-1.1,\
+ J2SE-1.2
+osgi.java.profile.name = J2SE-1.2
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile
new file mode 100644
index 0000000..b7dbdfa
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.3.profile
@@ -0,0 +1,63 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.omg.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3
+osgi.java.profile.name = J2SE-1.3
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile
new file mode 100644
index 0000000..5655989
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.4.profile
@@ -0,0 +1,123 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4
+osgi.java.profile.name = J2SE-1.4
diff --git a/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile b/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile
new file mode 100644
index 0000000..5c084ea
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/J2SE-1.5.profile
@@ -0,0 +1,150 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.activity,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = J2SE-1.5
diff --git a/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile b/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile
new file mode 100644
index 0000000..f335899
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/JavaSE-1.6.profile
@@ -0,0 +1,185 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.activation,\
+ javax.activity,\
+ javax.annotation,\
+ javax.annotation.processing,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.jws,\
+ javax.jws.soap,\
+ javax.lang.model,\
+ javax.lang.model.element,\
+ javax.lang.model.type,\
+ javax.lang.model.util,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.script,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.tools,\
+ javax.transaction,\
+ javax.transaction.xa,\
+ javax.xml,\
+ javax.xml.bind,\
+ javax.xml.bind.annotation,\
+ javax.xml.bind.annotation.adapters,\
+ javax.xml.bind.attachment,\
+ javax.xml.bind.helpers,\
+ javax.xml.bind.util,\
+ javax.xml.crypto,\
+ javax.xml.crypto.dom,\
+ javax.xml.crypto.dsig,\
+ javax.xml.crypto.dsig.dom,\
+ javax.xml.crypto.dsig.keyinfo,\
+ javax.xml.crypto.dsig.spec,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.soap,\
+ javax.xml.stream,\
+ javax.xml.stream.events,\
+ javax.xml.stream.util,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.ws,\
+ javax.xml.ws.handler,\
+ javax.xml.ws.handler.soap,\
+ javax.xml.ws.http,\
+ javax.xml.ws.soap,\
+ javax.xml.ws.spi,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ javax.*,\
+ org.ietf.jgss,\
+ org.omg.*,\
+ org.w3c.*,\
+ org.xml.*,\
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ JRE-1.1,\
+ J2SE-1.2,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5,\
+ JavaSE-1.6
+osgi.java.profile.name = JavaSE-1.6
diff --git a/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile
new file mode 100644
index 0000000..d8c2d3f
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.0.profile
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages =
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0
+osgi.java.profile.name = OSGi/Minimum-1.0
diff --git a/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile
new file mode 100644
index 0000000..2d8b661
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/OSGi_Minimum-1.1.profile
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.osgi.framework.system.packages =
+org.osgi.framework.bootdelegation = \
+ sun.*,\
+ com.sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1
+osgi.java.profile.name = OSGi/Minimum-1.1
diff --git a/sigil/org.cauldron.bld.core/profiles/profile.list b/sigil/org.cauldron.bld.core/profiles/profile.list
new file mode 100644
index 0000000..bea1b25
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/profiles/profile.list
@@ -0,0 +1,21 @@
+###############################################################################
+# Copyright (c) 2003, 2005 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+java.profiles = \
+ JavaSE-1.6.profile,\
+ J2SE-1.5.profile,\
+ J2SE-1.4.profile,\
+ J2SE-1.3.profile,\
+ J2SE-1.2.profile,\
+ JRE-1.1.profile,\
+ CDC-1.1_Foundation-1.1.profile,\
+ CDC-1.0_Foundation-1.0.profile,\
+ OSGi_Minimum-1.0.profile,\
+ OSGi_Minimum-1.1.profile,\
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java
new file mode 100644
index 0000000..6f6a33e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/bnd/BundleBuilder.java
@@ -0,0 +1,800 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.bnd;
+
+import static java.lang.String.format;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.jar.Attributes;
+
+import org.cauldron.bld.config.BldAttr;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.repository.SystemRepositoryProvider;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+import aQute.lib.osgi.Builder;
+import aQute.lib.osgi.Constants;
+import aQute.lib.osgi.Jar;
+import aQute.lib.osgi.Processor;
+
+public class BundleBuilder {
+ public static final String COMPONENT_ACTIVATOR_PKG = "XXX-FIXME-XXX";
+ public static final String COMPONENT_ACTIVATOR = COMPONENT_ACTIVATOR_PKG + ".Activator";
+ public static final String[] COMPONENT_ACTIVATOR_DEPS = { "XXX-FIXME-XXX",
+ "org.osgi.framework", "org.osgi.util.tracker" };
+ public static final String COMPONENT_DIR = "META-INF/XXX-FIXME-XXX";
+ public static final String COMPONENT_FLAG = "Installable-Component";
+ public static final String COMPONENT_LIST = "Installable-Component-Templates";
+
+ private IBldProject project;
+ private File[] classpath;
+ private String destPattern;
+ private Properties env;
+ private List<String> errors = new ArrayList<String>();
+ private List<String> warnings = new ArrayList<String>();
+
+ private Set<String> unused = new HashSet<String>();
+ private String lastBundle = null;
+
+ private boolean addMissingImports;
+ private boolean omitUnusedImports;
+ private String defaultPubtype;
+ private String codebaseFormat;
+ private Set<String> systemPkgs;
+
+ public interface Log {
+ void warn(String msg);
+
+ void verbose(String msg);
+ }
+
+ /**
+ * creates a BundleBuilder.
+ *
+ * @param classpath
+ * @param destPattern
+ * ivy-like pattern: PATH/[id].[ext] [id] is replaced with the
+ * bundle id. [name] is replaced with the Bundle-SymbolicName
+ * [ext] is replaced with "jar".
+ * @param hashtable
+ */
+ public BundleBuilder(IBldProject project, File[] classpath, String destPattern, Properties env) {
+ this.project = project;
+ this.classpath = classpath;
+ this.destPattern = destPattern;
+ this.env = env;
+
+ Properties options = project.getOptions();
+
+ addMissingImports = options.containsKey(BldAttr.OPTION_ADD_IMPORTS)
+ && Boolean.parseBoolean(options.getProperty(BldAttr.OPTION_ADD_IMPORTS));
+ omitUnusedImports = options.containsKey(BldAttr.OPTION_OMIT_IMPORTS)
+ && Boolean.parseBoolean(options.getProperty(BldAttr.OPTION_OMIT_IMPORTS));
+
+ defaultPubtype = options.getProperty(BldAttr.PUBTYPE_ATTRIBUTE, "rmi.codebase");
+
+ codebaseFormat = options.getProperty("codebaseFormat",
+ "cds://%1$s?bundle.symbolic.name=%2$s&type=%3$s");
+
+ for (IBldBundle b : project.getBundles()) {
+ lastBundle = b.getId();
+ for (IPackageImport import1 : b.getImports()) {
+ if (import1.getOSGiImport().equals(IPackageImport.OSGiImport.AUTO)) {
+ unused.add(import1.getPackageName());
+ }
+ }
+ }
+
+ try {
+ systemPkgs = new HashSet<String>();
+ Properties profile = SystemRepositoryProvider.readProfile(null);
+ String pkgs = profile.getProperty("org.osgi.framework.system.packages");
+ for (String pkg : pkgs.split(",\\s*")) {
+ systemPkgs.add(pkg);
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public List<String> errors() {
+ return errors;
+ }
+
+ public List<String> warnings() {
+ return warnings;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void convertErrors(String prefix, List messages) {
+ // TODO: make error mapping more generic
+ final String jarEmpty = "The JAR is empty";
+
+ for (Object omsg : messages) {
+ if (jarEmpty.equals(omsg))
+ warnings.add(prefix + omsg);
+ else
+ errors.add(prefix + omsg);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void convertWarnings(String prefix, List messages) {
+ for (Object omsg : messages) {
+ warnings.add(prefix + omsg);
+ }
+ }
+
+ public boolean createBundle(IBldBundle bundle, boolean force, Log log) throws Exception {
+ int bracket = destPattern.indexOf('[');
+ if (bracket < 0) {
+ throw new Exception("destPattern MUST contain [id] or [name].");
+ }
+
+ String dest = destPattern.replaceFirst("\\[id\\]", bundle.getId());
+ dest = dest.replaceFirst("\\[name\\]", bundle.getSymbolicName());
+ dest = dest.replaceFirst("\\[ext\\]", "jar");
+
+ bracket = dest.indexOf('[');
+ if (bracket >= 0) {
+ String token = dest.substring(bracket);
+ throw new Exception("destPattern: expected [id] or [name]: " + token);
+ }
+
+ errors.clear();
+ warnings.clear();
+
+ if (!bundle.getDownloadContents().isEmpty()) {
+ // create dljar
+ Properties dlspec = new Properties();
+ StringBuilder sb = new StringBuilder();
+
+ for (String pkg : bundle.getDownloadContents()) {
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append(pkg);
+ }
+
+ dlspec.setProperty(Constants.PRIVATE_PACKAGE, sb.toString());
+ dlspec.setProperty(Constants.BUNDLE_NAME, "Newton download jar");
+ dlspec.setProperty(Constants.NOEXTRAHEADERS, "true");
+ // stop it being a bundle, so cds doesn't scan it
+ dlspec.setProperty(Constants.REMOVE_HEADERS, Constants.BUNDLE_SYMBOLICNAME);
+
+ Builder builder = new Builder();
+ builder.setProperties(dlspec);
+ builder.setClasspath(classpath);
+
+ Jar dljar = builder.build();
+ convertErrors("BND (dljar): ", builder.getErrors());
+ convertWarnings("BND (dljar): ", builder.getWarnings());
+
+ String dldest = dest.replaceFirst("\\.jar$", "-dl.jar");
+ File dloutput = new File(dldest);
+ if (!dloutput.exists() || dloutput.lastModified() <= dljar.lastModified() || force) {
+ // jar.write(dldest) catches and ignores IOException
+ OutputStream out = new FileOutputStream(dldest);
+ dljar.write(out);
+ out.close();
+ dljar.close();
+ // XXX deleting dljar causes it to be rebuilt each time
+ // XXX but leaving it mean it may be installed where it's not
+ // wanted/needed.
+ dloutput.deleteOnExit();
+ }
+ builder.close();
+ }
+
+ Properties spec = getBndSpec(bundle, dest);
+
+ if (log != null) {
+ log.verbose("BND instructions: " + spec.toString());
+ }
+
+ Builder builder = new Builder();
+ builder.setPedantic(true);
+ builder.setProperties(spec);
+ builder.mergeProperties(env, false);
+
+ builder.setClasspath(classpath);
+ // builder.setSourcepath(sourcepath);
+
+ Jar jar = builder.build();
+
+ convertErrors("BND: ", builder.getErrors());
+ convertWarnings("BND: ", builder.getWarnings());
+
+ augmentImports(builder, jar, bundle);
+
+ if (log != null) {
+ for (String warn : warnings) {
+ log.warn(warn);
+ }
+ }
+
+ if (!errors.isEmpty()) {
+ throw new Exception(errors.toString());
+ }
+
+ boolean modified = false;
+ File output = new File(dest);
+
+ if (!output.exists() || force || (output.lastModified() <= jar.lastModified())
+ || (output.lastModified() <= project.getLastModified())) {
+ modified = true;
+ // jar.write(dest) catches and ignores IOException
+ OutputStream out = new FileOutputStream(dest);
+ jar.write(out);
+ out.close();
+ jar.close();
+ }
+
+ builder.close();
+
+ return modified;
+ }
+
+ private void augmentImports(Builder builder, Jar jar, IBldBundle bundle) throws IOException {
+ Attributes main = jar.getManifest().getMainAttributes();
+ String impHeader = main.getValue(Constants.IMPORT_PACKAGE);
+ Map<String, Map<String, String>> bndImports = Processor.parseHeader(impHeader, builder);
+
+ if (bndImports.isEmpty())
+ return;
+
+ ArrayList<String> self = new ArrayList<String>();
+ ArrayList<String> missing = new ArrayList<String>();
+ ArrayList<String> modified = new ArrayList<String>();
+ ArrayList<String> unversioned = new ArrayList<String>();
+
+ String expHeader = main.getValue(Constants.EXPORT_PACKAGE);
+ Set<String> bndExports = Processor.parseHeader(expHeader, builder).keySet();
+
+ HashMap<String, IPackageImport> imports = new HashMap<String, IPackageImport>();
+ for (IPackageImport pi : getImports(bundle)) {
+ switch (pi.getOSGiImport()) {
+ case NEVER:
+ break;
+ case ALWAYS:
+ String pkg = pi.getPackageName();
+ if (!bndImports.containsKey(pkg)) {
+ // Bnd doesn't think this import is needed - but we know
+ // better
+ HashMap<String, String> attrs = new HashMap<String, String>();
+ attrs.put(BldAttr.VERSION_ATTRIBUTE, pi.getVersions().toString());
+ bndImports.put(pkg, attrs);
+ modified.add(pkg + ";resolve=runtime");
+ }
+ // fall thru */
+ case AUTO:
+ imports.put(pi.getPackageName(), pi);
+ break;
+ }
+ }
+
+ boolean importDot = false;
+
+ for (String pkg : bndImports.keySet()) {
+ unused.remove(pkg);
+ Map<String, String> attrs = bndImports.get(pkg);
+ String currentVersion = (String) attrs.get(BldAttr.VERSION_ATTRIBUTE);
+ IPackageImport pi = imports.get(pkg);
+
+ if (pi != null) {
+ VersionRange range = pi.getVersions();
+ String version = range.toString();
+
+ if (!version.equals(currentVersion) && !range.equals(VersionRange.ANY_VERSION)) {
+ attrs.put(BldAttr.VERSION_ATTRIBUTE, version);
+ if (pi.isOptional())
+ attrs.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
+ modified
+ .add(pkg + ";version=" + version + (pi.isOptional() ? ";optional" : ""));
+ } else if ((currentVersion == null) && !systemPkgs.contains(pkg)) {
+ unversioned.add(pkg);
+ }
+ } else {
+ // bnd added the import ...
+ if (currentVersion == null) {
+ String defaultVersion = project.getDefaultPackageVersion(pkg);
+ if (defaultVersion != null) {
+ attrs.put(BldAttr.VERSION_ATTRIBUTE, defaultVersion);
+ currentVersion = defaultVersion;
+ }
+ }
+
+ String imp = pkg + (currentVersion == null ? "" : ";version=" + currentVersion);
+ if (bndExports.contains(pkg)) {
+ self.add(imp);
+ } else {
+ if (pkg.equals(".")) {
+ warnings.add("Bnd wants to import '.' (ignored)");
+ importDot = true;
+ } else {
+ missing.add(imp);
+ }
+ }
+ }
+ }
+
+ if (!modified.isEmpty() || importDot) {
+ if (importDot)
+ bndImports.remove(".");
+ // warnings.add("INFO: sigil modified imports: " + modified);
+ main.putValue(Constants.IMPORT_PACKAGE, Processor.printClauses(bndImports,
+ "resolution:"));
+ }
+
+ if (!self.isEmpty()) {
+ // warnings.add("INFO: added self imports: " + self);
+ }
+
+ if (!missing.isEmpty()) {
+ warnings.add("missing imports (added): " + missing);
+ }
+
+ if (!unversioned.isEmpty()) {
+ warnings.add("unversioned imports: " + unversioned);
+ }
+
+ if (bundle.getId().equals(lastBundle)) {
+ if (!unused.isEmpty()) {
+ warnings.add("unused imports (omitted): " + unused);
+ }
+ }
+ }
+
+ public Properties getBndSpec(IBldBundle bundle, String dest) throws IOException {
+ Properties spec = new Properties();
+
+ String junkHeaders = Constants.INCLUDE_RESOURCE; // shows local build
+ // paths; can be
+ // verbose
+ junkHeaders += "," + Constants.PRIVATE_PACKAGE; // less useful, as we
+ // use it for exported
+ // content too.
+
+ spec.setProperty(Constants.REMOVE_HEADERS, junkHeaders);
+ spec.setProperty(Constants.NOEXTRAHEADERS, "true"); // Created-By,
+ // Bnd-LastModified
+ // and Tool
+ spec.setProperty(Constants.CREATED_BY, "sigil.codecauldron.org");
+
+ Properties headers = bundle.getHeaders();
+ // XXX: catch attempts to set headers that conflict with Bnd
+ // instructions we generate?
+ spec.putAll(headers);
+
+ spec.setProperty(Constants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
+ spec.setProperty(Constants.BUNDLE_VERSION, bundle.getVersion());
+
+ String activator = bundle.getActivator();
+ if (activator != null)
+ spec.setProperty(Constants.BUNDLE_ACTIVATOR, activator);
+
+ addRequirements(bundle, spec);
+
+ List<String> exports = addExports(bundle, spec);
+
+ String composites = addResources(bundle, spec);
+
+ ArrayList<String> contents = new ArrayList<String>();
+ contents.addAll(bundle.getContents());
+
+ if (contents.isEmpty()) {
+ if (!project.getSourcePkgs().isEmpty()) {
+ contents.addAll(project.getSourcePkgs());
+ } else {
+ contents.addAll(exports);
+ }
+ }
+
+ if (composites.length() > 0) {
+ if (spec.containsKey(Constants.BUNDLE_ACTIVATOR))
+ warnings.add("-activator ignored when -composites specified.");
+ spec.setProperty(Constants.BUNDLE_ACTIVATOR, COMPONENT_ACTIVATOR);
+ spec.setProperty(COMPONENT_FLAG, "true");
+ spec.setProperty(COMPONENT_LIST, composites);
+ // add activator pkg directly, to avoid needing to add jar to
+ // Bundle-ClassPath.
+ // split-package directive needed to stop Bnd whinging when using
+ // other bundles containing the component-activator.
+ contents.add(COMPONENT_ACTIVATOR_PKG + ";-split-package:=merge-first");
+ }
+
+ List<String> srcPkgs = addLibs(bundle, dest, spec);
+
+ contents.addAll(srcPkgs);
+ addContents(contents, spec);
+
+ IRequiredBundle fh = bundle.getFragmentHost();
+ if (fh != null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(fh.getSymbolicName());
+ addVersions(fh.getVersions(), sb);
+ spec.setProperty(Constants.FRAGMENT_HOST, sb.toString());
+ }
+
+ return spec;
+ }
+
+ private void addContents(List<String> contents, Properties spec) {
+ // add contents
+ StringBuilder sb = new StringBuilder();
+ for (String pkg : contents) {
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append(pkg);
+ }
+
+ if (sb.length() > 0)
+ spec.setProperty(Constants.PRIVATE_PACKAGE, sb.toString());
+ }
+
+ private void appendProperty(String key, String value, Properties p) {
+ String list = p.getProperty(key);
+
+ if (list == null) {
+ list = value;
+ } else {
+ list = list + "," + value;
+ }
+
+ p.setProperty(key, list);
+ }
+
+ private List<String> addLibs(IBldBundle bundle, String dest, Properties spec)
+ throws IOException {
+ // final String cleanVersion =
+ // Builder.cleanupVersion(bundle.getVersion());
+ final String name = bundle.getSymbolicName();
+
+ ArrayList<String> srcPkgs = new ArrayList<String>();
+ Map<String, Map<String, String>> libs = bundle.getLibs();
+
+ if (!bundle.getDownloadContents().isEmpty()) {
+ // implicitly add dljar
+ File fdest = new File(dest);
+ String dlname = fdest.getName().replaceFirst("\\.jar$", "-dl.jar");
+
+ HashMap<String, String> attr = new HashMap<String, String>();
+ attr.put(BldAttr.KIND_ATTRIBUTE, "codebase");
+ attr.put(BldAttr.PUBLISH_ATTRIBUTE, dlname);
+
+ HashMap<String, Map<String, String>> lib2 = new HashMap<String, Map<String, String>>();
+ lib2.putAll(libs);
+ lib2.put(dlname, attr);
+ libs = lib2;
+ }
+
+ StringBuilder items = new StringBuilder();
+
+ for (String jarpath : libs.keySet()) {
+ Map<String, String> attr = libs.get(jarpath);
+ String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
+ String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
+
+ // first find the lib ..
+ String path = attr.get(BldAttr.PATH_ATTRIBUTE);
+ if (path == null)
+ path = jarpath;
+
+ File fsPath = bundle.resolve(path);
+
+ if (!fsPath.exists()) {
+ // try destDir
+ File destDir = new File(dest).getParentFile();
+ File file = new File(destDir, fsPath.getName());
+
+ if (!file.exists()) {
+ // try searching classpath
+ file = findInClasspathDir(fsPath.getName());
+ }
+
+ if (file != null && file.exists())
+ fsPath = file;
+ }
+
+ if (!fsPath.exists()) {
+ // XXX: find external bundle using name and version range?
+ // For now just let BND fail when it can't find resource.
+ }
+
+ appendProperty(Constants.INCLUDE_RESOURCE, jarpath + "=" + fsPath, spec);
+
+ if ("classpath".equals(kind)) {
+ String bcp = spec.getProperty(Constants.BUNDLE_CLASSPATH);
+ if (bcp == null || bcp.length() == 0)
+ spec.setProperty(Constants.BUNDLE_CLASSPATH, ".");
+ appendProperty(Constants.BUNDLE_CLASSPATH, jarpath, spec);
+ }
+
+ if (publish != null) {
+ String pubtype = attr.get(BldAttr.PUBTYPE_ATTRIBUTE);
+ if (pubtype == null)
+ pubtype = defaultPubtype;
+
+ if ("codebase".equals(kind)) {
+ String codebase = format(codebaseFormat, publish, name, pubtype);
+ String zone = attr.get(BldAttr.ZONE_ATTRIBUTE);
+ if (zone != null)
+ codebase += "&zone=" + zone;
+ appendProperty("RMI-Codebase", codebase, spec);
+ }
+
+ // add item to publish xml
+ items.append(format("<item name=\"%s\" path=\"%s\">\n", publish, jarpath));
+ items.append(format("<attribute name=\"type\" value=\"%s\"/>\n", pubtype));
+ items.append("</item>\n");
+ }
+ }
+
+ if (items.length() > 0) {
+ File publishFile = new File(dest.replaceFirst("\\.jar$", "-publish.xml"));
+ publishFile.deleteOnExit();
+ PrintWriter writer = new PrintWriter(new FileWriter(publishFile));
+
+ writer.println("<publish>");
+ writer.println(format("<attribute name=\"bundle.symbolic.name\" value=\"%s\"/>", name));
+ writer.print(items.toString());
+ writer.println("</publish>");
+ writer.close();
+
+ appendProperty(Constants.INCLUDE_RESOURCE, "publish.xml=" + publishFile, spec);
+ }
+
+ return srcPkgs;
+ }
+
+ private String addResources(IBldBundle bundle, Properties spec) {
+ Map<String, String> resources = bundle.getResources();
+ StringBuilder composites = new StringBuilder();
+
+ for (String composite : bundle.getComposites()) {
+ File path = bundle.resolve(composite);
+ String name = path.getName();
+
+ String bPath = COMPONENT_DIR + "/" + name;
+ resources.put(bPath, path.getPath());
+
+ if (composites.length() > 0)
+ composites.append(",");
+ composites.append(bPath);
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ for (String bPath : resources.keySet()) {
+ if (bPath.startsWith("@")) { // Bnd in-line jar
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append('@');
+ sb.append(bundle.resolve(bPath.substring(1)));
+ continue;
+ }
+
+ String fsPath = resources.get(bPath);
+ if ("".equals(fsPath))
+ fsPath = bPath;
+
+ File resolved = bundle.resolve(fsPath);
+
+ // fsPath may contain Bnd variable, making path appear to not exist
+
+ if (!resolved.exists()) {
+ // Bnd already looks for classpath jars
+ File found = findInClasspathDir(fsPath);
+ if (found != null) {
+ fsPath = found.getPath();
+ } else {
+ fsPath = resolved.getAbsolutePath();
+ }
+ } else {
+ fsPath = resolved.getAbsolutePath();
+ }
+
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append(bPath);
+ sb.append('=');
+ sb.append(fsPath);
+ }
+
+ if (sb.length() > 0)
+ spec.setProperty(Constants.INCLUDE_RESOURCE, sb.toString());
+
+ return composites.toString();
+ }
+
+ private List<IPackageImport> getImports(IBldBundle bundle) {
+ List<IPackageImport> imports = bundle.getImports();
+ Set<String> pkgs = new HashSet<String>();
+
+ for (IPackageImport pi : imports) {
+ pkgs.add(pi.getPackageName());
+ }
+
+ // add component activator imports
+ if (!bundle.getComposites().isEmpty()) {
+ for (String pkg : BundleBuilder.COMPONENT_ACTIVATOR_DEPS) {
+ if (pkgs.contains(pkg))
+ continue;
+ PackageImport pi = new PackageImport();
+ pi.setPackageName(pkg);
+ String versions = project.getDefaultPackageVersion(pkg);
+ if (versions != null)
+ pi.setVersions(VersionRange.parseVersionRange(versions));
+ imports.add(pi);
+ }
+ }
+
+ return imports;
+ }
+
+ private void addRequirements(IBldBundle bundle, Properties spec) {
+ StringBuilder sb = new StringBuilder();
+
+ // option;addMissingImports=true
+ // Lets Bnd calculate imports (i.e. specify *),
+ // which are then examined by augmentImports();
+
+ // option;omitUnusedImports=true (implies addMissingImports=true)
+ // When project contains multiple bundles which don't all use all
+ // imports,
+ // avoids warnings like:
+ // "Importing packages that are never referred to by any class on the Bundle-ClassPath"
+
+ if (omitUnusedImports && !addMissingImports) {
+ warnings.add("omitUnusedImports ignored as addMissingImports=false.");
+ omitUnusedImports = false;
+ }
+
+ List<IPackageImport> imports = getImports(bundle);
+
+ sb.setLength(0);
+
+ // allow existing header;Package-Import to specify ignored packages
+ sb.append(spec.getProperty(Constants.IMPORT_PACKAGE, ""));
+
+ for (IPackageImport pi : imports) {
+ switch (pi.getOSGiImport()) {
+ case AUTO:
+ if (omitUnusedImports)
+ continue; // added by Import-Package: * and fixed by
+ // augmentImports()
+ break;
+ case NEVER:
+ if (pi.isDependency())
+ continue; // resolve=compile
+ break;
+ case ALWAYS:
+ // Bnd will probably whinge that this import is not used.
+ // we omit it here and replace it in augmentImports,
+ // but only if addMissingImports is true;
+ // otherwise, if the import is used, Bnd will fail.
+ if (addMissingImports)
+ continue;
+ break;
+ }
+
+ if (sb.length() > 0)
+ sb.append(",");
+
+ if (pi.getOSGiImport().equals(IPackageImport.OSGiImport.NEVER)) {
+ sb.append("!");
+ sb.append(pi.getPackageName());
+ } else {
+ sb.append(pi.getPackageName());
+ addVersions(pi.getVersions(), sb);
+
+ if (pi.isOptional()) {
+ sb.append(";resolution:=optional");
+ }
+ }
+ }
+
+ if (addMissingImports) {
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append("*");
+ }
+
+ spec.setProperty(Constants.IMPORT_PACKAGE, sb.toString());
+
+ sb.setLength(0);
+ for (IRequiredBundle rb : bundle.getRequires()) {
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append(rb.getSymbolicName());
+ addVersions(rb.getVersions(), sb);
+ }
+
+ if (sb.length() > 0) {
+ spec.setProperty(Constants.REQUIRE_BUNDLE, sb.toString());
+ }
+ }
+
+ private List<String> addExports(IBldBundle bundle, Properties spec) {
+ List<IPackageExport> exports = bundle.getExports();
+ ArrayList<String> list = new ArrayList<String>();
+ StringBuilder sb = new StringBuilder();
+
+ for (IPackageExport export : exports) {
+ if (sb.length() > 0)
+ sb.append(",");
+ sb.append(export.getPackageName());
+ if (!export.getVersion().equals(Version.emptyVersion)) {
+ sb.append(";version=\"");
+ sb.append(export.getVersion());
+ sb.append("\"");
+ }
+ list.add(export.getPackageName());
+ }
+
+ if (sb.length() > 0) {
+ // EXPORT_CONTENTS just sets the Export-Package manifest header;
+ // it doesn't add contents like EXPORT_PACKAGE does.
+ spec.setProperty(Constants.EXPORT_CONTENTS, sb.toString());
+ }
+
+ return list;
+ }
+
+ private void addVersions(VersionRange range, StringBuilder sb) {
+ if (!range.equals(VersionRange.ANY_VERSION)) {
+ sb.append(";version=\"");
+ sb.append(range);
+ sb.append("\"");
+ }
+ }
+
+ private File findInClasspathDir(String file) {
+ for (File cp : classpath) {
+ if (cp.isDirectory()) {
+ File path = new File(cp, file);
+ if (path.exists()) {
+ return path;
+ }
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java
new file mode 100644
index 0000000..7496121
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldAttr.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+public class BldAttr {
+ // Sigil attributes
+
+ public static final String KIND_ATTRIBUTE = "kind";
+
+ public static final String RESOLVE_ATTRIBUTE = "resolve";
+ public static final String RESOLVE_AUTO = "auto";
+ public static final String RESOLVE_COMPILE = "compile";
+ public static final String RESOLVE_RUNTIME = "runtime";
+ public static final String RESOLVE_IGNORE = "ignore";
+
+ public static final String PUBLISH_ATTRIBUTE = "publish";
+ public static final String PUBTYPE_ATTRIBUTE = "type";
+ public static final String PATH_ATTRIBUTE = "path";
+ public static final Object ZONE_ATTRIBUTE = "zone";
+
+ // Sigil options
+
+ public static final String OPTION_ADD_IMPORTS = "addMissingImports";
+ public static final String OPTION_OMIT_IMPORTS = "omitUnusedImports";
+
+ // OSGi attributes
+
+ public static final String RESOLUTION_ATTRIBUTE = "resolution";
+ public static final String RESOLUTION_OPTIONAL = "optional";
+
+ public static final String VERSION_ATTRIBUTE = "version";
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java
new file mode 100644
index 0000000..225406a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConfig.java
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.cauldron.bld.core.util.QuoteUtil;
+
+public class BldConfig {
+
+ // control properties
+ public static final String C_BUNDLES = "-bundles";
+ public static final String C_REPOSITORIES = "-repositories";
+
+ // string properties
+ public static final String S_ACTIVATOR = "-activator";
+ public static final String S_DEFAULTS = "-defaults";
+ public static final String S_ID = "id";
+ public static final String S_SYM_NAME = "name";
+ public static final String S_VERSION = "version";
+ public static final String[] STRING_KEYS = { S_ACTIVATOR, S_DEFAULTS, S_ID, S_SYM_NAME, S_VERSION };
+
+ // list properties
+ public static final String L_COMPOSITES = "-composites";
+ public static final String L_CONTENTS = "-contents";
+ public static final String L_DL_CONTENTS = "-downloads";
+ public static final String L_SRC_CONTENTS = "-sourcedirs";
+ public static final String L_RESOURCES = "-resources";
+ public static final String[] LIST_KEYS = {
+ L_COMPOSITES, L_CONTENTS, L_DL_CONTENTS, L_SRC_CONTENTS, L_RESOURCES };
+
+ // map properties
+ public static final String M_EXPORTS = "-exports";
+ public static final String M_IMPORTS = "-imports";
+ public static final String M_REQUIRES = "-requires";
+ public static final String M_FRAGMENT = "-fragment";
+ public static final String M_LIBS = "-libs";
+ public static final String[] MAP_KEYS = { M_EXPORTS, M_IMPORTS, M_REQUIRES, M_FRAGMENT, M_LIBS };
+
+ // property properties
+ public static final String P_HEADER = "header";
+ public static final String P_OPTION = "option";
+ public static final String P_PACKAGE_VERSION = "package";
+ public static final String P_BUNDLE_VERSION = "bundle";
+ public static final String[] PROP_KEYS = { P_HEADER, P_OPTION, P_PACKAGE_VERSION, P_BUNDLE_VERSION };
+
+ // private constants
+ private static final String LIST_REGEX = ",\\s*";
+ private static final String MAPATTR_REGEX = ";\\s*";
+ private static final String MAPATTR_SEP = ";";
+ private static final String SUBKEY_SEP = ";";
+
+ // configuration is stored in typed maps
+ private Map<String, String> string = new TreeMap<String, String>();
+ private Map<String, List<String>> list = new TreeMap<String, List<String>>();
+ private Map<String, Map<String, Map<String, String>>> map = new TreeMap<String, Map<String,Map<String,String>>>();
+ private Map<String, BldConfig> config = new TreeMap<String, BldConfig>();
+ private Map<String, Properties> property = new TreeMap<String, Properties>();
+
+ // default config - not modified or saved
+ private BldConfig dflt;
+
+ private Properties unknown = new Properties();
+ private String comment = "";
+
+ public BldConfig() {
+ }
+
+ public BldConfig(Properties p) throws IOException {
+ merge(p);
+ }
+
+ public void setDefault(BldConfig dflt) {
+ this.dflt = dflt;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public Properties getUnknown() {
+ return unknown;
+ }
+
+ public String getString(String id, String key) {
+ if (id != null && config.containsKey(id)) {
+ String value = config.get(id).getString(null, key);
+ if (value != null)
+ return value;
+ }
+ return string.containsKey(key) ? string.get(key) : (dflt != null ? dflt.getString(id, key) : null);
+ }
+
+ public List<String> getList(String id, String key) {
+ if (id != null && config.containsKey(id)) {
+ List<String> value = config.get(id).getList(null, key);
+ if (value != null)
+ return value;
+ }
+ return list.containsKey(key) ? list.get(key) : (dflt != null ? dflt.getList(id, key) : Collections.<String>emptyList());
+ }
+
+ public Map<String, Map<String,String>> getMap(String id, String key) {
+ if (id != null && config.containsKey(id)) {
+ Map<String, Map<String,String>> value = config.get(id).getMap(null, key);
+ if (value != null)
+ return value;
+ }
+ return map.containsKey(key) ? map.get(key)
+ : (dflt != null ? dflt.getMap(id, key) : Collections.<String, Map<String,String>>emptyMap());
+ }
+
+ public void setString(String id, String key, String value) {
+ if (!value.equals(getString(id, key))) {
+ if (id != null) {
+ if (!config.containsKey(id))
+ config.put(id, new BldConfig());
+ config.get(id).setString(null, key, value);
+ } else {
+ String dval = (dflt == null ? dflt.getString(null, key) : null);
+ if (value.equals("") && (dval == null || dval.equals(""))) {
+ string.remove(key);
+ } else {
+ string.put(key, value);
+ }
+ }
+ }
+ }
+
+ public void setList(String id, String key, List<String> value) {
+ if (!value.equals(getList(id, key))) {
+ if (id != null) {
+ if (!config.containsKey(id))
+ config.put(id, new BldConfig());
+ config.get(id).setList(null, key, value);
+ } else if (value.isEmpty() && (dflt == null || dflt.getList(null, key).isEmpty())) {
+ list.remove(key);
+ } else {
+ list.put(key, value);
+ }
+ }
+ }
+
+ public void setMap(String id, String key, Map<String, Map<String,String>> value) {
+ if (!value.equals(getMap(id, key))) {
+ if (id != null) {
+ if (!config.containsKey(id))
+ config.put(id, new BldConfig());
+ config.get(id).setMap(null, key, value);
+ } else if (value.isEmpty() && (dflt == null || dflt.getMap(null, key).isEmpty())) {
+ map.remove(key);
+ } else {
+ map.put(key, value);
+ }
+ }
+ }
+
+ public Properties getProps(String id, String key) {
+ // merge main and sub properties
+ Properties props = new Properties();
+
+ if (dflt != null)
+ props.putAll(dflt.getProps(id, key));
+
+ if (property.containsKey(key))
+ props.putAll(property.get(key));
+
+ if (id != null && config.containsKey(id)) {
+ Properties p2 = config.get(id).getProps(null, key);
+ if (p2 != null)
+ props.putAll(p2);
+ }
+
+ return props;
+ }
+
+ // only sets one property at a time
+ public void setProp(String id, String key, String k2, String v2) {
+ if (v2 == null)
+ return;
+ Properties props = getProps(id, key);
+ if (!v2.equals(props.getProperty(key))) {
+ if (id != null) {
+ if (!config.containsKey(id))
+ config.put(id, new BldConfig());
+ config.get(id).setProp(null, key, k2, v2);
+ } else {
+ if (property.containsKey(key)) {
+ property.get(key).put(k2, v2);
+ } else {
+ Properties value = new Properties();
+ value.put(k2, v2);
+ property.put(key, value);
+ }
+ }
+ }
+ }
+
+ /**
+ * write config in Property file format.
+ * This allows us to make it prettier than Properties.store().
+ */
+ public void write(final PrintWriter out) {
+ out.println(comment);
+
+ // Note: don't add date stamp, or file will differ each time it's saved.
+ out.println("# sigil project file, saved by plugin.\n");
+
+ dump("", new Properties() {
+ private static final long serialVersionUID = 1L; //appease eclipse
+ @Override
+ public Object put(Object key, Object value) {
+ if (value instanceof String) {
+ out.println(key + ": " + value);
+ out.println("");
+ } else if (value instanceof List) {
+ out.println(key + ": \\");
+ for (Object k : (List<?>) value) {
+ out.println("\t" + k + ", \\");
+ }
+ out.println("");
+ }
+ else if (value instanceof Map) {
+ out.println(key + ": \\");
+ StringBuilder b = new StringBuilder();
+ for (Map.Entry<?, ?> e : ((Map<?,?>) value).entrySet()) {
+ b.append("\t");
+ b.append(e.getKey());
+ Map<?, ?> v = (Map<?, ?>) e.getValue();
+ if (!v.isEmpty()) {
+ for (Map.Entry<?, ?> e2 : v.entrySet()) {
+ b.append(MAPATTR_SEP);
+ b.append(e2.getKey());
+ b.append("=");
+ String v2 = e2.getValue().toString();
+ if (v2.contains(",")) {
+ b.append("\"");
+ b.append(v2);
+ b.append("\"");
+ }
+ else {
+ b.append(v2);
+ }
+ }
+ }
+ b.append (", \\\n");
+ }
+ out.println(b.toString());
+ }
+ return null;
+ }
+ });
+ out.println("# end");
+ }
+
+ /**
+ * dump config in pseudo Properties format.
+ * Note: some values are not Strings (they're List<String>).
+ */
+ private void dump(String prefix, Properties p) {
+ for (String key : string.keySet()) {
+ p.put(prefix + key, string.get(key));
+ }
+
+ for (String key : list.keySet()) {
+ List<String> list2 = list.get(key);
+ p.put(prefix + key, list2);
+ }
+
+ for (String key : map.keySet()) {
+ Map<String, Map<String,String>> map2 = map.get(key);
+ p.put(prefix + key, map2);
+ }
+
+ for (String key : property.keySet()) {
+ Properties props = property.get(key);
+ for (Object k2 : props.keySet()) {
+ p.put(prefix + key + SUBKEY_SEP + k2, props.get(k2));
+ }
+ }
+
+ for (String key : config.keySet()) {
+ BldConfig config2 = config.get(key);
+ config2.dump(key + SUBKEY_SEP + prefix, p);
+ }
+
+ for (Object key : unknown.keySet()) {
+ String value = unknown.getProperty((String)key);
+ if (value.length() > 0)
+ p.put(prefix + key, value);
+ }
+ }
+
+ /**
+ * merges properties into current configuration.
+ * @param p
+ * @throws IOException
+ */
+ public void merge(Properties p) throws IOException {
+ if (p.isEmpty())
+ return;
+
+ final List<String> strings = Arrays.asList(STRING_KEYS);
+ final List<String> lists = Arrays.asList(LIST_KEYS);
+ final List<String> maps = Arrays.asList(MAP_KEYS);
+
+ List<String> bundleKeys = new ArrayList<String>();
+ List<String> repoKeys = new ArrayList<String>();
+
+ String bundles = p.getProperty(C_BUNDLES);
+ if (bundles != null) {
+ bundleKeys.addAll(Arrays.asList(bundles.split(LIST_REGEX)));
+ list.put(C_BUNDLES, bundleKeys);
+ }
+
+ String repos = p.getProperty(C_REPOSITORIES);
+ if (repos != null) {
+ for ( String s : repos.split(LIST_REGEX) ) {
+ repoKeys.add(s.trim());
+ }
+ list.put(C_REPOSITORIES, repoKeys);
+ }
+
+ List<String> subKeys = new ArrayList<String>();
+ subKeys.addAll(Arrays.asList(PROP_KEYS));
+ subKeys.addAll(bundleKeys);
+ subKeys.addAll(repoKeys);
+
+ Map<String, Properties> sub = new TreeMap<String, Properties>();
+
+ for (Object k : p.keySet()) {
+ String key = (String) k;
+ if (key.equals(C_BUNDLES) || key.equals(C_REPOSITORIES))
+ continue;
+
+ String value = p.getProperty(key);
+ String[] keys = key.split(SUBKEY_SEP, 2);
+
+ if (keys.length > 1) {
+ Properties p2 = sub.get(keys[0]);
+ if (p2 == null) {
+ p2 = new Properties();
+ sub.put(keys[0], p2);
+ if (!subKeys.contains(keys[0])) {
+ unknown.setProperty(keys[0] + SUBKEY_SEP, "");
+ }
+ }
+ p2.setProperty(keys[1], value);
+ } else if (strings.contains(key)) {
+ if (!string.containsKey(key))
+ string.put(key, value);
+ } else if (lists.contains(key)) {
+ if (!list.containsKey(key)) {
+ ArrayList<String> list2 = new ArrayList<String>();
+ for (String s : value.split(LIST_REGEX)) {
+ if ( s.trim().length() > 0 ) {
+ list2.add(s.trim());
+ }
+ }
+ if ( !list2.isEmpty() ) {
+ list.put(key, list2);
+ }
+ }
+ } else if (maps.contains(key)) {
+ if (!map.containsKey(key)) {
+ Map<String, Map<String,String>> map2 = new TreeMap<String, Map<String,String>>();
+
+ for (String subValue : QuoteUtil.split(value)) {
+ if (subValue.trim().length() > 0) {
+ String[] split = subValue.split(MAPATTR_REGEX);
+ Map<String,String> map3 = new TreeMap<String,String>();
+ for (int i = 1; i < split.length; ++i){
+ String[] keyVal = split[i].split(":?=", 2);
+ if (keyVal.length != 2) {
+ throw new IOException("attribute missing '=':" + subValue);
+ }
+ map3.put(keyVal[0], keyVal[1]);
+ }
+ map2.put(split[0], map3);
+ }
+ }
+
+ map.put(key, map2);
+ }
+ } else {
+ unknown.setProperty(key, value);
+ }
+ }
+
+ for (String subKey : sub.keySet()) {
+ Properties props = sub.get(subKey);
+ if (!props.isEmpty()) {
+ if (bundleKeys.contains(subKey)) {
+ BldConfig config2 = new BldConfig(props);
+ Properties unkProps = config2.getUnknown();
+
+ if (config2.map.containsKey(M_IMPORTS))
+ unkProps.setProperty(M_IMPORTS, "");
+
+ if (config2.map.containsKey(M_REQUIRES))
+ unkProps.setProperty(M_REQUIRES, "");
+
+ for (Object unk : unkProps.keySet()) {
+ unknown.setProperty(subKey + SUBKEY_SEP + unk, "");
+ }
+ config.put(subKey, config2);
+ } else {
+ property.put(subKey, props);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "string: " + string + " list:" + list + " map: " + map + " prop: " + property + " config:" + config;
+ }
+
+}
+
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java
new file mode 100644
index 0000000..0f8a332
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldConverter.java
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cauldron.bld.config.IBldProject.IBldBundle;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Version;
+
+import aQute.lib.osgi.Constants;
+
+public class BldConverter {
+ private static final String classpathFormat = "<classpathentry kind=\"%s\" path=\"%s\"/>";
+ private BldConfig config;
+ private Properties packageDefaults;
+ private TreeSet<String> packageWildDefaults;
+
+ public BldConverter(BldConfig config) {
+ this.config = config;
+ }
+
+ /**
+ * converts to an ISigilBundle.
+ * @param id
+ * @param bundle
+ * @return
+ */
+ public ISigilBundle getBundle(String id, IBldBundle bundle) {
+
+ ISigilBundle sigilBundle = new SigilBundle();
+ IBundleModelElement info = new BundleModelElement();
+ sigilBundle.setBundleInfo(info);
+
+ // exports
+ // FIXME: UI doesn't understand export wildcard packages
+ for (IPackageExport export : bundle.getExports()) {
+ IPackageExport clone = (IPackageExport) export.clone();
+ clone.setParent(null);
+ info.addExport(clone);
+ }
+
+ // imports
+ for (IPackageImport import1 : bundle.getImports()) {
+ IPackageImport clone = (IPackageImport) import1.clone();
+ clone.setParent(null);
+ info.addImport(clone);
+ }
+
+ // requires
+ for (IRequiredBundle require : bundle.getRequires()) {
+ IRequiredBundle clone = (IRequiredBundle) require.clone();
+ clone.setParent(null);
+ info.addRequiredBundle(clone);
+ }
+
+ // fragment
+ IRequiredBundle fragment = bundle.getFragmentHost();
+ if (fragment != null) {
+ info.setFragmentHost(fragment);
+ }
+
+ // contents
+ for (String pkg : bundle.getContents()) {
+ sigilBundle.addPackage(pkg);
+ }
+
+ // downloads
+ for (String pkg : bundle.getDownloadContents()) {
+ sigilBundle.addDownloadPackage(pkg);
+ }
+
+ // sources
+ for (String source : config.getList(null, BldConfig.L_SRC_CONTENTS) ) {
+ sigilBundle.addClasspathEntry(String.format(classpathFormat, "src", source));
+ }
+
+ // libs
+ Map<String, Map<String, String>> libs = bundle.getLibs();
+
+ for (String path : libs.keySet()) {
+ Map<String, String> attr = libs.get(path);
+ String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
+ String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
+
+ if (publish != null) {
+ // FIXME: UI doesn't understand publish=name
+ BldCore.error("Can't convert -libs publish=" + publish);
+ continue;
+ }
+
+ if ("classpath".equals(kind)) {
+ sigilBundle.addClasspathEntry(String.format(classpathFormat, "lib", path));
+ } else {
+ BldCore.error("Can't convert -libs kind=" + kind);
+ }
+ }
+
+ // resources
+ // FIXME: UI doesn't support -resources: path1=path2
+ Map<String, String> resources = bundle.getResources();
+ for (String resource : resources.keySet()) {
+ String fsPath = resources.get(resource);
+ if (!"".equals(fsPath)) {
+ BldCore.error("FIXME: can't convert resource: " + resource + "=" + fsPath);
+ }
+ sigilBundle.addSourcePath(new Path(resource));
+ }
+
+ ////////////////////
+ // simple headers
+
+ info.setSymbolicName(bundle.getSymbolicName());
+
+ info.setVersion(Version.parseVersion(bundle.getVersion()));
+
+ String activator = bundle.getActivator();
+ if (activator != null)
+ info.setActivator(activator);
+
+ Properties headers = config.getProps(id, BldConfig.P_HEADER);
+ String header;
+
+ header = headers.getProperty("CATEGORY");
+ if (header != null)
+ info.setCategory(header);
+
+ header = headers.getProperty(Constants.BUNDLE_CONTACTADDRESS);
+ if (header != null)
+ info.setContactAddress(header);
+
+ header = headers.getProperty(Constants.BUNDLE_COPYRIGHT);
+ if (header != null)
+ info.setCopyright(header);
+
+ header = headers.getProperty(Constants.BUNDLE_DESCRIPTION);
+ if (header != null)
+ info.setDescription(header);
+
+ header = headers.getProperty(Constants.BUNDLE_VENDOR);
+ if (header != null)
+ info.setVendor(header);
+
+ header = headers.getProperty(Constants.BUNDLE_NAME);
+ if (header != null)
+ info.setName(header);
+
+ header = headers.getProperty(Constants.BUNDLE_DOCURL);
+ if (header != null)
+ info.setDocURI(URI.create(header));
+
+ header = headers.getProperty(Constants.BUNDLE_LICENSE);
+ if (header != null)
+ info.setDocURI(URI.create(header));
+
+ return sigilBundle;
+ }
+
+ private VersionRange defaultVersion(VersionRange current, String defaultRange) {
+ if (current.equals(VersionRange.ANY_VERSION) ||
+ current.equals(VersionRange.parseVersionRange(defaultRange))) {
+ return null;
+ }
+ return current;
+ }
+
+ // FIXME - copied from BldProject
+ private String getDefaultPackageVersion(String name) {
+ if (packageDefaults == null) {
+ packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+ packageWildDefaults = new TreeSet<String>();
+
+ for (Object key : packageDefaults.keySet()) {
+ String pkg = (String)key;
+ if (pkg.endsWith("*")) {
+ packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
+ }
+ }
+ }
+
+ String version = packageDefaults.getProperty(name);
+
+ if (version == null) {
+ for (String pkg : packageWildDefaults) {
+ if (name.startsWith(pkg)) {
+ version = packageDefaults.getProperty(pkg + "*");
+ // break; -- don't break, as we want the longest match
+ }
+ }
+ }
+
+ return version;
+ }
+
+
+ /**
+ * converts from an ISigilBundle.
+ *
+ * @param id
+ * @param bundle
+ */
+ public void setBundle(String id, ISigilBundle bundle) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ String bundleVersion = config.getString(id, BldConfig.S_VERSION);
+
+ // exports
+ Map<String, Map<String, String>> exports = new TreeMap<String, Map<String,String>>();
+ for (IPackageExport export : info.getExports()) {
+ Map<String, String> map2 = new TreeMap<String, String>();
+ String version = export.getVersion().toString();
+ if (!version.equals(bundleVersion))
+ map2.put(BldAttr.VERSION_ATTRIBUTE, version);
+ exports.put(export.getPackageName(), map2);
+ }
+
+ if (!exports.isEmpty() || !config.getMap(id, BldConfig.M_EXPORTS).isEmpty()) {
+ config.setMap(id, BldConfig.M_EXPORTS, exports);
+ }
+
+ // imports
+ Map<String, Map<String, String>> imports = new TreeMap<String, Map<String,String>>();
+
+ // FIXME: default version logic is wrong here
+ // if the version to be saved is the same as the default version,
+ // then we should _remove_ the version from the value being saved,
+ // since config.getMap() does not apply default versions.
+ for (IPackageImport import1 : info.getImports()) {
+ Map<String, String> map2 = new TreeMap<String, String>();
+ String name = import1.getPackageName();
+ VersionRange versions = defaultVersion(import1.getVersions(), getDefaultPackageVersion(name));
+
+ boolean isDependency = import1.isDependency();
+ Map<String, String> selfImport = exports.get(name);
+
+ if (selfImport != null) {
+ // avoid saving self-import attributes, e.g.
+ // org.cauldron.newton.example.fractal.engine;resolve=auto;version=1.0.0
+ isDependency = true;
+
+ if (versions != null) {
+ String exportVersion = selfImport.get(BldAttr.VERSION_ATTRIBUTE);
+ if (exportVersion == null)
+ exportVersion = bundleVersion;
+
+ if (exportVersion.equals(versions.toString())) {
+ versions = null;
+ }
+ }
+ }
+
+ if (versions != null) {
+ map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+ }
+
+ if (import1.isOptional()) {
+ map2.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
+ }
+
+ String resolve = BldProject.getResolve(import1, isDependency);
+ if (resolve != null)
+ map2.put(BldAttr.RESOLVE_ATTRIBUTE, resolve);
+
+ imports.put(name, map2);
+ }
+ if (!imports.isEmpty() || !config.getMap(id, BldConfig.M_IMPORTS).isEmpty()) {
+ config.setMap(id, BldConfig.M_IMPORTS, imports);
+ }
+
+ // requires
+ Properties defaultBundles = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
+ Map<String, Map<String, String>> requires = new TreeMap<String, Map<String,String>>();
+
+ for (IRequiredBundle require : info.getRequiredBundles()) {
+ Map<String, String> map2 = new TreeMap<String, String>();
+ String name = require.getSymbolicName();
+ VersionRange versions = defaultVersion(require.getVersions(), defaultBundles.getProperty(name));
+ if (versions != null)
+ map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+ requires.put(name, map2);
+ }
+ if (!requires.isEmpty() || !config.getMap(id, BldConfig.M_REQUIRES).isEmpty()) {
+ config.setMap(id, BldConfig.M_REQUIRES, requires);
+ }
+
+ // fragment
+ Map<String, Map<String, String>> fragments = new TreeMap<String, Map<String,String>>();
+ IRequiredBundle fragment = info.getFragmentHost();
+ if (fragment != null) {
+ Map<String, String> map2 = new TreeMap<String, String>();
+ String name = fragment.getSymbolicName();
+ VersionRange versions = defaultVersion(fragment.getVersions(), defaultBundles.getProperty(name));
+ if (versions != null)
+ map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
+ fragments.put(name, map2);
+ }
+ if (!fragments.isEmpty() || !config.getMap(id, BldConfig.M_FRAGMENT).isEmpty()) {
+ config.setMap(id, BldConfig.M_FRAGMENT, fragments);
+ }
+
+ // contents
+ List<String> contents = new ArrayList<String>();
+ for (String pkg : bundle.getPackages()) {
+ contents.add(pkg);
+ }
+ if (!contents.isEmpty() || !config.getList(id, BldConfig.L_CONTENTS).isEmpty()) {
+ config.setList(id, BldConfig.L_CONTENTS, contents);
+ }
+
+ // dl contents
+ List<String> dlcontents = new ArrayList<String>();
+ for (String pkg : bundle.getDownloadPackages()) {
+ dlcontents.add(pkg);
+ }
+ if (!dlcontents.isEmpty() || !config.getList(id, BldConfig.L_DL_CONTENTS).isEmpty()) {
+ config.setList(id, BldConfig.L_DL_CONTENTS, dlcontents);
+ }
+
+ // libs
+ Map<String, Map<String, String>> libs = new TreeMap<String, Map<String,String>>();
+ List<String> sources = new ArrayList<String>();
+
+ // classpathEntries map to -libs or -sources
+ for (String entry : bundle.getClasspathEntrys()) {
+ // <classpathentry kind="lib" path="lib/dependee.jar"/>
+ // <classpathentry kind="src" path="src"/>
+ final String regex = ".* kind=\"([^\"]+)\" path=\"([^\"]+)\".*";
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(entry);
+ if (matcher.matches()) {
+ String kind = matcher.group(1);
+ String path = matcher.group(2);
+ if (kind.equals("lib")) {
+ Map<String, String> map2 = new TreeMap<String, String>();
+ map2.put(BldAttr.KIND_ATTRIBUTE, "classpath");
+ libs.put(path, map2);
+ } else if (kind.equals("src")) {
+ sources.add(path);
+ } else {
+ BldCore.error("unknown classpathentry kind=" + kind);
+ }
+ } else {
+ BldCore.error("can't match classpathEntry in: " + entry);
+ }
+ }
+
+ if (!libs.isEmpty() || !config.getMap(id, BldConfig.M_LIBS).isEmpty()) {
+ config.setMap(id, BldConfig.M_LIBS, libs);
+ }
+
+ if (!sources.isEmpty() || !config.getList(id, BldConfig.L_SRC_CONTENTS).isEmpty()) {
+ config.setList(id, BldConfig.L_SRC_CONTENTS, sources);
+ }
+
+ // composites
+ ArrayList<String> composites = new ArrayList<String>();
+ for (ISCAComposite composite : bundle.getComposites()) {
+ String path = composite.getLocation().toString();
+ // TODO relativize
+ composites.add(path);
+ }
+
+ if (!composites.isEmpty() || !config.getList(id, BldConfig.L_COMPOSITES).isEmpty()) {
+ Collections.sort(composites);
+ config.setList(id, BldConfig.L_COMPOSITES, composites);
+ }
+
+ // resources
+ ArrayList<String> resources = new ArrayList<String>();
+ for (IPath ipath : bundle.getSourcePaths()) {
+ resources.add(ipath.toString());
+ }
+
+ if (!resources.isEmpty() || !config.getList(id, BldConfig.L_RESOURCES).isEmpty()) {
+ Collections.sort(resources);
+ config.setList(id, BldConfig.L_RESOURCES, resources);
+ }
+
+ if (info.getSourceLocation() != null) {
+ BldCore.error("SourceLocation conversion not yet implemented.");
+ }
+
+ if (!info.getLibraryImports().isEmpty()) {
+ BldCore.error("LibraryImports conversion not yet implemented.");
+ }
+
+ ////////////////////
+ // simple headers
+
+ List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
+ String idBsn = id != null ? id : ids.get(0);
+ String oldBsn = config.getString(id, BldConfig.S_SYM_NAME);
+ String bsn = info.getSymbolicName();
+
+ if (!bsn.equals(idBsn) || oldBsn != null)
+ config.setString(id, BldConfig.S_SYM_NAME, bsn);
+
+ String version = info.getVersion().toString();
+ if (version != null)
+ config.setString(id, BldConfig.S_VERSION, version);
+
+ String activator = info.getActivator();
+ if (activator != null)
+ config.setString(id, BldConfig.S_ACTIVATOR, activator);
+
+ Properties headers = config.getProps(null, BldConfig.P_HEADER);
+
+ setHeader(headers, id, "CATEGORY", info.getCategory());
+ setHeader(headers, id, Constants.BUNDLE_CONTACTADDRESS, info.getContactAddress());
+ setHeader(headers, id, Constants.BUNDLE_COPYRIGHT, info.getCopyright());
+ setHeader(headers, id, Constants.BUNDLE_DESCRIPTION, info.getDescription());
+ setHeader(headers, id, Constants.BUNDLE_VENDOR, info.getVendor());
+ setHeader(headers, id, Constants.BUNDLE_NAME, info.getName());
+
+ if (info.getDocURI() != null)
+ config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_DOCURL, info.getDocURI().toString());
+
+ if (info.getLicenseURI() != null)
+ config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_LICENSE, info.getLicenseURI().toString());
+ }
+
+ private void setHeader(Properties headers, String id, String key, String value) {
+ if (value == null)
+ value = "";
+ if (!value.equals(headers.getProperty(key, "")))
+ config.setProp(id, BldConfig.P_HEADER, key, value);
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java
new file mode 100644
index 0000000..a02adccc
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public class BldFactory {
+ private static Map<URI, BldProject> projects = new HashMap<URI, BldProject>();
+
+ public static IBldProject getProject(URI uri) throws IOException {
+ return getProject(uri, false);
+ }
+
+ public static IBldProject getProject(URI uri, boolean ignoreCache) throws IOException {
+ return load(uri, ignoreCache);
+ }
+
+ public static IRepositoryConfig getConfig(URI uri) throws IOException {
+ return load(uri, false);
+ }
+
+ /**
+ * creates a new project file, initialised with defaults.
+ * @param uri where the file will be saved - used to resolve relative paths.
+ * @param defaults relative path to defaults file - default ../sigil.properties.
+ * @return
+ * @throws IOException
+ */
+ public static IBldProject newProject(URI uri, String defaults) throws IOException {
+ BldProject project = new BldProject(uri);
+ Properties p = new Properties();
+ if (defaults != null)
+ p.setProperty(BldConfig.S_DEFAULTS, defaults);
+ project.loadDefaults(p);
+ return project;
+ }
+
+ private static BldProject load(URI uri, boolean ignoreCache) throws IOException {
+ BldProject p = null;
+ if (!ignoreCache) {
+ p = projects.get(uri);
+ }
+
+ if (p == null) {
+ p = new BldProject(uri);
+ p.load();
+ projects.put(uri, p);
+
+ if (Boolean.getBoolean("org.cauldron.bld.config.test")) {
+ File path = new File(uri.getPath() + ".tmp");
+ System.out.println("XXX: config.test writing: " + path);
+ p.saveAs(path);
+ }
+ }
+ return p;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java
new file mode 100644
index 0000000..585a029
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldProject.java
@@ -0,0 +1,823 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeSet;
+
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageExport;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport.OSGiImport;
+import org.osgi.framework.Version;
+
+public class BldProject implements IBldProject, IRepositoryConfig {
+ private static final String OVERRIDE_PREFIX = "sigil.";
+ private static final int MAX_HEADER = 10240;
+ // cache to avoid loading the same default config for each project
+ private static Map<URL, BldConfig> defaultsCache = new HashMap<URL, BldConfig>();
+ private static Properties overrides;
+
+ private List<String> sourcePkgs;
+ private BldConfig config;
+ private BldConverter convert;
+ private BundleModelElement requirements;
+ private File baseDir;
+ private URI loc;
+ private Properties packageDefaults;
+ private TreeSet<String> packageWildDefaults;
+ private long lastModified;
+
+ /* package */BldProject(URI relLoc) {
+ config = new BldConfig();
+ convert = new BldConverter(config);
+ loc = new File(".").toURI().resolve(relLoc).normalize();
+ File f = new File(loc);
+ lastModified = f.lastModified();
+ baseDir = f.getParentFile();
+ }
+
+ /* package */void load() throws IOException {
+ // allow System property overrides, e.g.
+ // ANT_OPTS='-Dsigil.option\;addMissingImports=false' ant
+ config.merge(getOverrides());
+
+ InputStream in = null;
+ try {
+ in = loc.toURL().openStream();
+ BufferedInputStream bis = new BufferedInputStream(in);
+ bis.mark(MAX_HEADER);
+ readHeader(bis);
+ bis.reset();
+
+ Properties p = new Properties();
+ p.load(bis);
+ config.merge(p);
+
+ Properties unknown = config.getUnknown();
+ if (!unknown.isEmpty())
+ System.err.println("WARN: unknown keys " + unknown.keySet() + " in " + loc);
+
+ loadDefaults(p);
+ requirements = parseRequirements();
+ }
+ finally {
+ if ( in != null ) {
+ in.close();
+ }
+ }
+ }
+
+ /* package */void loadDefaults(Properties p) throws IOException {
+ BldConfig c = loadDefaults(p, baseDir, null);
+ config.setDefault(c);
+
+ Properties options = config.getProps(null, BldConfig.P_OPTION);
+
+ if (!options.containsKey(BldAttr.OPTION_ADD_IMPORTS))
+ c.setProp(null, BldConfig.P_OPTION, BldAttr.OPTION_ADD_IMPORTS, "true");
+
+ // default omitUnusedImports option depends on number of bundles...
+ // we set it here to avoid it being written by save(),
+ // but as this may alter cached defaults, once set we have to reset it
+ // for each project.
+
+ boolean omitSet = options.containsKey("__omit_set__");
+ boolean multiple = getBundleIds().size() > 1;
+
+ if (multiple || omitSet) {
+ if (!options.containsKey(BldAttr.OPTION_OMIT_IMPORTS) || omitSet) {
+ c.setProp(null, BldConfig.P_OPTION, BldAttr.OPTION_OMIT_IMPORTS, multiple + "");
+ c.setProp(null, BldConfig.P_OPTION, "__omit_set__", "true");
+ }
+ }
+ }
+
+ private synchronized BldConfig loadDefaults(Properties props, File base, BldConfig dflt)
+ throws IOException {
+ boolean cached = false;
+ String defaults = props.getProperty(BldConfig.S_DEFAULTS, "-"
+ + IBldProject.PROJECT_DEFAULTS);
+
+ if (base != null && defaults.length() > 0) {
+ boolean ignore = defaults.startsWith("-");
+
+ if (ignore)
+ defaults = defaults.substring(1);
+
+ try {
+ File file = new File(base, defaults).getCanonicalFile();
+ URL url = file.toURL();
+
+ if (dflt == null) {
+ dflt = defaultsCache.get(url);
+ if (dflt != null)
+ return dflt;
+
+ dflt = new BldConfig();
+ defaultsCache.put(url, dflt);
+ cached = true;
+ }
+
+ Properties p = new Properties();
+ p.load(url.openStream());
+ dflt.merge(p);
+
+ ignore = false;
+ loadDefaults(p, file.getParentFile(), dflt);
+ } catch (IOException e) {
+ if (!ignore)
+ throw e;
+ }
+ }
+
+ if (dflt == null)
+ return new BldConfig();
+
+ if (cached) {
+ Properties unknown = dflt.getUnknown();
+ if (!unknown.isEmpty())
+ System.err.println("WARN: unknown keys " + unknown.keySet() + " in defaults for "
+ + loc);
+ }
+
+ return dflt;
+ }
+
+ private static Properties getOverrides() {
+ if (overrides == null) {
+ overrides = new Properties();
+ Properties sysProps = System.getProperties();
+
+ for (Object okey : sysProps.keySet()) {
+ String key = (String) okey;
+ if (key.startsWith(OVERRIDE_PREFIX)) {
+ overrides.setProperty(key.substring(OVERRIDE_PREFIX.length()), sysProps
+ .getProperty(key));
+ }
+ }
+ }
+
+ return overrides;
+ }
+
+ private void readHeader(InputStream in) throws IOException {
+ BufferedReader r = new BufferedReader(new InputStreamReader(in));
+ StringBuffer header = new StringBuffer();
+ String line;
+ while ((line = r.readLine()) != null) {
+ if (line.startsWith("#")) {
+ header.append(line);
+ header.append("\n");
+ } else {
+ config.setComment(header.toString());
+ break;
+ }
+ }
+ }
+
+ public File resolve(String path) {
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ // can't use loc.resolve(value), as value may not be valid URI.
+ file = new File(baseDir, path);
+ }
+ return file;
+ }
+
+ public String getVersion() {
+ String version = config.getString(null, BldConfig.S_VERSION);
+ return version == null ? "0" : version;
+ }
+
+ public IBundleModelElement getDependencies() {
+ IBundleModelElement dependencies = new BundleModelElement();
+
+ for (IModelElement element : getRequirements().children()) {
+ if (element instanceof IPackageImport) {
+ IPackageImport import1 = (IPackageImport) element;
+ if (!import1.isDependency())
+ continue;
+
+ IPackageImport pi = (IPackageImport) (element.clone());
+ pi.setParent(null);
+ dependencies.addImport(pi);
+ } else {
+ IRequiredBundle rb = (IRequiredBundle) (element.clone());
+ rb.setParent(null);
+ dependencies.addRequiredBundle(rb);
+ }
+ }
+
+ boolean containsComposite = false;
+
+ for (IBldBundle bundle : getBundles()) {
+ if (!bundle.getComposites().isEmpty()) {
+ containsComposite = true;
+ break;
+ }
+ }
+
+ // add dependency on component activator
+ if (containsComposite) {
+ PackageImport pi = new PackageImport();
+ pi.setPackageName(BundleBuilder.COMPONENT_ACTIVATOR_PKG);
+ pi.setOSGiImport(OSGiImport.NEVER);
+ dependencies.addImport(pi);
+ }
+
+ return dependencies;
+ }
+
+ private IBundleModelElement getRequirements() {
+ return requirements;
+ }
+
+ /*
+ * private boolean globMatch(String pkg, Set<String> set) { // exact match
+ * if (set.contains(pkg)) return true;
+ *
+ * // org.foo.bar matches org.foo. for (String glob : set) { if
+ * (glob.matches(pkg)) { return true; } }
+ *
+ * return false; }
+ */
+
+ /**
+ * set internal OSGiImport and isDependency flags, based on external
+ * resolution= attribute.
+ *
+ * OSGiImport: AUTO ALWAYS NEVER dependency: default - compile !dependency:
+ * auto runtime ignore
+ *
+ */
+ private void setResolve(IPackageImport pi, String resolve) throws IOException {
+ if (pi.isOptional())
+ pi.setDependency(false);
+
+ if (BldAttr.RESOLVE_COMPILE.equals(resolve)) {
+ if (pi.isOptional())
+ pi.setDependency(true);
+ else
+ pi.setOSGiImport(OSGiImport.NEVER);
+ } else if (BldAttr.RESOLVE_RUNTIME.equals(resolve)) {
+ pi.setDependency(false);
+ pi.setOSGiImport(OSGiImport.ALWAYS);
+ } else if (BldAttr.RESOLVE_AUTO.equals(resolve)) {
+ pi.setDependency(false);
+ } else if (BldAttr.RESOLVE_IGNORE.equals(resolve)) {
+ pi.setDependency(false);
+ pi.setOSGiImport(OSGiImport.NEVER);
+ } else if (resolve != null) {
+ throw new IOException("Bad attribute value: " + BldAttr.RESOLVE_ATTRIBUTE + "="
+ + resolve);
+ }
+ }
+
+ /**
+ * get external resolve= attribute from internal PackageImport flags. This
+ * is called from BldConverter.setBundle().
+ */
+ public static String getResolve(IPackageImport pi, boolean isDependency) {
+ OSGiImport osgiImport = pi.getOSGiImport();
+ String resolve = null;
+
+ if (isDependency) {
+ if (osgiImport.equals(OSGiImport.NEVER) || pi.isOptional())
+ resolve = BldAttr.RESOLVE_COMPILE;
+ } else {
+ switch (osgiImport) {
+ case ALWAYS:
+ resolve = BldAttr.RESOLVE_RUNTIME;
+ break;
+ case AUTO:
+ resolve = BldAttr.RESOLVE_AUTO;
+ break;
+ case NEVER:
+ resolve = BldAttr.RESOLVE_IGNORE;
+ break;
+ }
+ }
+ return resolve;
+ }
+
+ public String getDefaultPackageVersion(String name) {
+ if (packageDefaults == null) {
+ packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+ packageWildDefaults = new TreeSet<String>();
+
+ for (Object key : packageDefaults.keySet()) {
+ String pkg = (String) key;
+ if (pkg.endsWith("*")) {
+ packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
+ }
+ }
+ }
+
+ String version = packageDefaults.getProperty(name);
+
+ if (version == null) {
+ for (String pkg : packageWildDefaults) {
+ if (name.startsWith(pkg)) {
+ version = packageDefaults.getProperty(pkg + "*");
+ // break; -- don't break, as we want the longest match
+ }
+ }
+ }
+
+ return version;
+ }
+
+ private synchronized BundleModelElement parseRequirements() throws IOException {
+ BundleModelElement reqs = new BundleModelElement();
+
+ List<String> sourceContents = getSourcePkgs();
+ HashSet<String> exports = new HashSet<String>();
+
+ for (IBldBundle bundle : getBundles()) {
+ for (IPackageExport export : bundle.getExports()) {
+ exports.add(export.getPackageName());
+ }
+ }
+
+ Map<String, Map<String, String>> imports = config.getMap(null, BldConfig.M_IMPORTS);
+
+ for (String name : imports.keySet()) {
+ Map<String, String> attr = imports.get(name);
+
+ String resolve = attr.get(BldAttr.RESOLVE_ATTRIBUTE);
+ String resolution = attr.get(BldAttr.RESOLUTION_ATTRIBUTE);
+ String versions = attr.containsKey(BldAttr.VERSION_ATTRIBUTE) ? attr
+ .get(BldAttr.VERSION_ATTRIBUTE) : getDefaultPackageVersion(name);
+
+ PackageImport pi = new PackageImport();
+ pi.setPackageName(name);
+
+ // avoid dependency on self-exports
+ // XXX: BldConverter.setBundle contains similar logic
+ if (exports.contains(name)
+ && (sourceContents.contains(name) || sourceContents.isEmpty())) {
+ pi.setDependency(false);
+ if (versions == null)
+ versions = getVersion();
+ }
+
+ if (!checkVersionRange(versions)) {
+ throw new IOException("Failed to parse version range for " + resolve
+ + " missing \"'s around version range?");
+ }
+
+ pi.setVersions(VersionRange.parseVersionRange(versions));
+
+ if (BldAttr.RESOLUTION_OPTIONAL.equals(resolution)) {
+ pi.setOptional(true);
+ } else if (resolution != null) {
+ throw new IOException("Bad attribute value: " + BldAttr.RESOLUTION_ATTRIBUTE + "="
+ + resolution);
+ }
+
+ setResolve(pi, resolve);
+
+ reqs.addImport(pi);
+ }
+
+ Map<String, Map<String, String>> requires = config.getMap(null, BldConfig.M_REQUIRES);
+ Properties bundleDefaults = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
+
+ if (requires != null) {
+ for (String name : requires.keySet()) {
+ Map<String, String> attr = requires.get(name);
+ String versions = attr.containsKey(BldAttr.VERSION_ATTRIBUTE) ? attr
+ .get(BldAttr.VERSION_ATTRIBUTE) : bundleDefaults.getProperty(name);
+
+ RequiredBundle rb = new RequiredBundle();
+ rb.setSymbolicName(name);
+ rb.setVersions(VersionRange.parseVersionRange(versions));
+
+ reqs.addRequiredBundle(rb);
+ }
+ }
+
+ for (IBldBundle bundle : getBundles()) {
+ IRequiredBundle fh = bundle.getFragmentHost();
+ if (fh != null)
+ reqs.addRequiredBundle(fh);
+ }
+
+ return reqs;
+ }
+
+ private boolean checkVersionRange(String versions) {
+ if (versions == null || versions.length() == 0) {
+ return true;
+ } else {
+ switch (versions.charAt(0)) {
+ case '(':
+ case '[':
+ switch (versions.charAt(versions.length() - 1)) {
+ case ')':
+ case ']':
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return true;
+ }
+ }
+ }
+
+ public List<String> getBundleIds() {
+ List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
+ if (ids == null)
+ return Collections.emptyList();
+ return ids;
+ }
+
+ public List<IBldBundle> getBundles() {
+ ArrayList<IBldBundle> list = new ArrayList<IBldBundle>();
+
+ for (String id : getBundleIds()) {
+ list.add(new BldBundle(id));
+ }
+
+ return list;
+ }
+
+ // Implement IBldConfig: getRepositoryConfig
+
+ public Map<String, Properties> getRepositoryConfig() {
+ HashMap<String, Properties> map = new HashMap<String, Properties>();
+
+ final Map<String, String> env = System.getenv();
+ final Properties props = new Properties();
+ try {
+ // supports ${.} and ${..} expansions
+ props.setProperty(".", resolve(".").getCanonicalPath());
+ props.setProperty("..", resolve("..").getCanonicalPath());
+ } catch (IOException e) {
+ }
+
+ for (String name : config.getList(null, BldConfig.C_REPOSITORIES)) {
+ Properties repo = config.getProps(null, name);
+
+ for (Object k : repo.keySet()) {
+ String key = (String) k;
+ String value = repo.getProperty(key);
+
+ String expand = BldUtil.expand(value, new Properties() {
+ public String getProperty(String name) {
+ return props.getProperty(name, env.get(name));
+ }
+ });
+
+ if (!value.equals(expand)) {
+ value = expand;
+ repo.setProperty(key, value);
+ }
+
+ // backwards compatible support before ${.} and ${..} was added
+ if (value.startsWith("./") || value.startsWith("../")) {
+ try {
+ // need canonical path, to normalise
+ value = resolve(value).getCanonicalPath();
+ } catch (IOException e) {
+ }
+ repo.setProperty(key, value);
+ }
+ }
+
+ map.put(name, repo);
+ }
+ return map;
+ }
+
+ public Properties getOptions() {
+ return config.getProps(null, BldConfig.P_OPTION);
+ }
+
+ public Properties getDefaultPackageVersions() {
+ return config.getProps(null, BldConfig.P_PACKAGE_VERSION);
+ }
+
+ public ISigilBundle getDefaultBundle() {
+ List<String> bundles = getBundleIds();
+ if (bundles.isEmpty())
+ return null;
+
+ String id = bundles.get(0);
+ return getSigilBundle(id);
+ }
+
+ public ISigilBundle getSigilBundle(String id) {
+ BldBundle bundle = new BldBundle(id);
+ return convert.getBundle(id, bundle);
+ }
+
+ public void setDefaultBundle(ISigilBundle bundle) {
+ setSigilBundle(null, bundle);
+ }
+
+ public void setSigilBundle(String id, ISigilBundle bundle) {
+ List<String> ids = getBundleIds();
+
+ if (ids.isEmpty()) {
+ ArrayList<String> list = new ArrayList<String>();
+ list.add(id == null ? bundle.getBundleInfo().getSymbolicName() : id);
+ config.setList(null, BldConfig.C_BUNDLES, list);
+ } else if (id == null) {
+ id = ids.get(0);
+ } else if (!ids.contains(id)) {
+ List<String> list = config.getList(null, BldConfig.C_BUNDLES);
+ list.add(id);
+ config.setList(null, BldConfig.C_BUNDLES, list);
+ }
+
+ if (ids.size() == 1)
+ id = null; // don't prefix default bundle with id
+
+ convert.setBundle(id, bundle);
+ }
+
+ public void save() throws IOException {
+ saveAs(new File(loc));
+ }
+
+ public void saveAs(File path) throws IOException {
+ File part = new File(path.getPath() + ".part");
+ saveTo(new FileOutputStream((part)));
+
+ path.delete();
+ if (!part.renameTo(path))
+ throw new IOException("failed to rename " + part + " to " + path);
+ }
+
+ public void saveTo(OutputStream out) {
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(out));
+ config.write(writer);
+ writer.close();
+ }
+
+ public List<String> getSourceDirs() {
+ List<String> list = config.getList(null, BldConfig.L_SRC_CONTENTS);
+ return list != null ? list : Collections.<String> emptyList();
+ }
+
+ public List<String> getSourcePkgs() {
+ if (sourcePkgs == null) {
+ sourcePkgs = new ArrayList<String>();
+ for (String src : getSourceDirs()) {
+ File dir = resolve(src);
+ if (!dir.isDirectory()) {
+ System.err.println("WARN: sourcedir does not exist: " + dir);
+ continue;
+ // throw new RuntimeException("sourcedir: " + dir +
+ // " : is not a directory.");
+ }
+ findSrcPkgs(dir, null, sourcePkgs);
+ }
+ }
+
+ return sourcePkgs;
+ }
+
+ private void findSrcPkgs(File dir, String pkg, List<String> result) {
+ ArrayList<File> dirs = new ArrayList<File>();
+ boolean found = false;
+
+ for (String name : dir.list()) {
+ if (name.endsWith(".java")) {
+ found = true;
+ } else if (!name.equals(".svn")) {
+ File d = new File(dir, name);
+ if (d.isDirectory())
+ dirs.add(d);
+ }
+ }
+
+ if (pkg == null) {
+ pkg = "";
+ } else if (pkg.equals("")) {
+ pkg = dir.getName();
+ } else {
+ pkg = pkg + "." + dir.getName();
+ }
+
+ if (found)
+ result.add(pkg);
+
+ for (File d : dirs)
+ findSrcPkgs(d, pkg, result);
+ }
+
+ /**
+ * BldBundle
+ *
+ */
+ class BldBundle implements IBldBundle {
+ private String id;
+
+ public BldBundle(String id) {
+ this.id = id;
+ }
+
+ public File resolve(String path) {
+ return BldProject.this.resolve(path);
+ }
+
+ private String getString(String key) {
+ return config.getString(id, key);
+ }
+
+ private List<String> getList(String key) {
+ List<String> list = config.getList(id, key);
+ return list != null ? list : Collections.<String> emptyList();
+ }
+
+ private Map<String, Map<String, String>> getMap(String key) {
+ Map<String, Map<String, String>> map = config.getMap(id, key);
+ return map != null ? map : Collections.<String, Map<String, String>> emptyMap();
+ }
+
+ public String getActivator() {
+ return getString(BldConfig.S_ACTIVATOR);
+ }
+
+ public String getId() {
+ String name = getString("id");
+ return name != null ? name : id;
+ }
+
+ public String getVersion() {
+ String ver = getString(BldConfig.S_VERSION);
+ if (ver == null) {
+ ver = BldProject.this.getVersion();
+ }
+ return ver;
+ }
+
+ public String getSymbolicName() {
+ String name = getString(BldConfig.S_SYM_NAME);
+ return name != null ? name : getId();
+ }
+
+ public List<IPackageExport> getExports() {
+ ArrayList<IPackageExport> list = new ArrayList<IPackageExport>();
+ Map<String, Map<String, String>> exports = getMap(BldConfig.M_EXPORTS);
+
+ if (exports != null) {
+ for (String name : exports.keySet()) {
+ Map<String, String> attrs = exports.get(name);
+ PackageExport pkgExport = new PackageExport();
+ pkgExport.setPackageName(name);
+
+ String version = attrs.get(BldAttr.VERSION_ATTRIBUTE);
+ // only default export version from local packages
+ if (version == null
+ && (getSourcePkgs().isEmpty() || getSourcePkgs().contains(name))) {
+ version = getVersion();
+ }
+
+ if (version != null)
+ pkgExport.setVersion(new Version(version));
+
+ list.add(pkgExport);
+ }
+ }
+
+ return list;
+ }
+
+ public List<IPackageImport> getImports() {
+ ArrayList<IPackageImport> list = new ArrayList<IPackageImport>();
+
+ for (IPackageImport import1 : getRequirements().childrenOfType(IPackageImport.class)) {
+ list.add(import1);
+ }
+
+ return list;
+ }
+
+ public List<IRequiredBundle> getRequires() {
+ ArrayList<IRequiredBundle> list = new ArrayList<IRequiredBundle>();
+ list.addAll(Arrays.asList(getRequirements().childrenOfType(IRequiredBundle.class)));
+
+ for (IBldBundle bundle : getBundles()) {
+ IRequiredBundle fh = bundle.getFragmentHost();
+ if (fh != null)
+ list.remove(fh);
+ }
+
+ return list;
+ }
+
+ public IRequiredBundle getFragmentHost() {
+ IRequiredBundle fragment = null;
+ Map<String, Map<String, String>> fragments = getMap(BldConfig.M_FRAGMENT);
+ if (fragments != null) {
+ for (String name : fragments.keySet()) {
+ Map<String, String> attr = fragments.get(name);
+ String versions = attr.isEmpty() ? null : attr.get(BldAttr.VERSION_ATTRIBUTE);
+ fragment = new RequiredBundle();
+ fragment.setSymbolicName(name);
+ fragment.setVersions(VersionRange.parseVersionRange(versions));
+ break;
+ }
+ }
+
+ return fragment;
+ }
+
+ public Map<String, Map<String, String>> getLibs() {
+ Map<String, Map<String, String>> libs = getMap(BldConfig.M_LIBS);
+ return (libs != null) ? libs : Collections.<String, Map<String, String>> emptyMap();
+ }
+
+ public List<String> getContents() {
+ return getList(BldConfig.L_CONTENTS);
+ }
+
+ public List<String> getDownloadContents() {
+ return getList(BldConfig.L_DL_CONTENTS);
+ }
+
+ public List<String> getComposites() {
+ ArrayList<String> list = new ArrayList<String>();
+ for (String composite : getList(BldConfig.L_COMPOSITES)) {
+ list.add(composite);
+ }
+
+ return list;
+ }
+
+ public Map<String, String> getResources() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ List<String> resources = getList(BldConfig.L_RESOURCES);
+
+ if (resources != null) {
+ for (String resource : resources) {
+ String[] paths = resource.split("=", 2);
+ String fsPath = (paths.length > 1 ? paths[1] : "");
+ map.put(paths[0], fsPath);
+ }
+ }
+ return map;
+ }
+
+ public Properties getHeaders() {
+ Properties headers = config.getProps(id, BldConfig.P_HEADER);
+ return headers;
+ }
+
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java
new file mode 100644
index 0000000..ad85627
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/BldUtil.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+// taken from Newton Launcher
+
+public class BldUtil {
+ /**
+ * expands property references embedded in strings. Each occurrence of ${name} is replaced with the value of
+ * p.getProperty("name"); If the property is not set, then the original reference, is returned as follows "?<name>".
+ *
+ * Strings to be expanded should not contain $ or }, except to indicate expansions.
+ *
+ * Value is expanded recursively and so can contain further ${name} references. Also supports shell-expansions
+ * ${name:-value}, ${name:=value} and ${name:+value}.
+ *
+ * <pre>
+ * ${parameter}
+ * The value of parameter is substituted.
+ * ${parameter:-word}
+ * Use Default Values. If parameter is null, the expansion of word
+ * is substituted. Otherwise, the value of parameter is substituted.
+ * ${parameter:=word}
+ * Assign Default Values. If parameter is null, the expansion of
+ * word is assigned to parameter. The value of parameter is then
+ * substituted.
+ * ${parameter:+word}
+ * Use Alternate Value. If parameter is null, nothing is
+ * substituted, otherwise the expansion of word is substituted.
+ * ${parameter:?word}
+ * Raise Error. If parameter is null, a RuntimeException is thown,
+ * with word as the message.
+ * </pre>
+ */
+ public static String expand(String s, Properties p) {
+ // regex to match property references e.g. ${name}
+ // TODO this is very simplistic, so strings to be expanded should not
+ // contain $ or }, except where substitution is expected.
+ // Update: propRef regex now allows substitutions to contain $,
+ // e.g. where a Windows ${user.name} is $Admin or similar.
+ final Pattern propRef = Pattern.compile("\\$\\{(((\\$[^\\{\\}])|[^\\$\\}])+\\$?)\\}");
+ final Pattern backslash = Pattern.compile("\\\\");
+ final Pattern dollar = Pattern.compile("\\$");
+
+ if (s == null) {
+ return null;
+ }
+
+ if (s.indexOf("${") == -1) { // shortcut if no expansions
+ return s;
+ }
+
+ for (int i = 0; i < 20; i++) { // avoids self-referencing expansions
+ // System.out.println("XXX expand[" + i + "] = [" + s + "]");
+ Matcher matcher = propRef.matcher(s);
+
+ if (!matcher.find()) {
+ // replace unmatched items
+ s = s.replaceAll("\\Q??[\\E", "\\${");
+ s = s.replaceAll("\\Q??]\\E", "}");
+ // debug("expanded: " + s);
+ if (s.indexOf("${") != -1) {
+ throw new RuntimeException("Can't expand: " + s);
+ }
+ return s;
+ }
+
+ String key = matcher.group(1);
+ String[] keydef = key.split(":[=+-?@]", 2);
+ String replace;
+
+ if (keydef.length != 2) {
+ replace = key.length() == 0 ? null : p.getProperty(key);
+ }
+ else {
+ replace = keydef[0].length() == 0 ? null : p.getProperty(keydef[0]);
+
+ if (replace != null && (replace.length() == 0 || replace.indexOf("${") != -1)) {
+ // don't want unexpanded replacement, as it may stop ${...:-default}
+ replace = null;
+ }
+
+ if (key.indexOf(":+") != -1) {
+ replace = ((replace == null) ? "" : keydef[1]);
+ }
+ else if (replace == null) {
+ replace = keydef[1];
+
+ if (key.indexOf(":?") != -1) {
+ String msg = "${" + keydef[0] + ":?" + keydef[1] + "} property not set";
+ throw new RuntimeException(msg);
+ }
+
+ if (key.indexOf(":=") != -1) {
+ p.setProperty(keydef[0], keydef[1]);
+ }
+ }
+ }
+
+ if (replace == null) {
+ // TODO: this is a hack to avoid looping on unmatched references
+ // should really leave unchanged and process rest of string.
+ // We use "]" as delimiter to avoid non-matched "}"
+ // terminating potential _propRef match
+ replace = "??[" + key + "??]";
+ }
+
+ // Excerpt from replaceAll() javadoc:
+ //
+ // Note that backslashes (\) and dollar signs ($) in the replacement
+ // string may cause the results to be different than if it were
+ // being
+ // treated as a literal replacement string. Dollar signs may be
+ // treated
+ // as references to captured subsequences, and backslashes are used
+ // to
+ // escape literal characters in the replacement string.
+ // escape any \ or $ in replacement string
+ replace = backslash.matcher(replace).replaceAll("\\\\\\\\");
+ replace = dollar.matcher(replace).replaceAll("\\\\\\$");
+
+ s = s.replaceAll("\\Q${" + key + "}\\E", replace);
+ }
+
+ throw new RuntimeException("expand: loop expanding: " + s);
+ }
+
+ public static String expand(String s) {
+ final Map<String, String> env = System.getenv();
+
+ return expand(s, new Properties() {
+ public String getProperty(String name) {
+ return System.getProperty(name, env.get(name));
+ }
+ });
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java
new file mode 100644
index 0000000..b273b7b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IBldProject.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IBldProject {
+
+ static final String PROJECT_FILE = "sigil.properties";
+ static final String PROJECT_DEFAULTS = "../sigil-defaults.properties";
+
+ void save() throws IOException;
+
+ void saveAs(File path) throws IOException;
+
+ void saveTo(OutputStream out) throws IOException;
+
+ /**
+ * gets default package version ranges.
+ */
+ Properties getDefaultPackageVersions();
+
+ /**
+ * gets default package version range for named package.
+ * Also handles wildcards in defaults.
+ */
+ String getDefaultPackageVersion(String name);
+
+ /**
+ * get project options.
+ */
+ Properties getOptions();
+
+ /**
+ * get project version.
+ */
+ String getVersion();
+
+ /**
+ * gets dependencies (Package-Import and Require-Bundle) needed to compile.
+ */
+ IBundleModelElement getDependencies();
+
+ /**
+ * gets project source directories.
+ * This is a convenient way to specify bundle contents
+ * when the project doesn't contains multiple bundles.
+ */
+ List<String> getSourceDirs();
+
+ /**
+ * gets the list of packages represented by getSourceDirs().
+ * @throws IOException
+ */
+ List<String> getSourcePkgs();
+
+ /**
+ * gets bundle ids.
+ */
+ List<String> getBundleIds();
+
+ /**
+ * gets bundles.
+ */
+ List<IBldBundle> getBundles();
+
+ /**
+ * convert specified bundle to SigilBundle.
+ */
+ ISigilBundle getSigilBundle(String id);
+
+ /**
+ * convert SigilBundle to specified bundle.
+ */
+ void setSigilBundle(String id, ISigilBundle sb);
+
+ /**
+ * converts default bundle to SigilBundle.
+ */
+ ISigilBundle getDefaultBundle();
+
+ /**
+ * converts SigilBundle to default bundle.
+ */
+ void setDefaultBundle(ISigilBundle sb);
+
+ /**
+ * resolves a relative path against the project file location.
+ */
+ File resolve(String path);
+
+ /**
+ * gets the last modification date of the project file.
+ */
+ long getLastModified();
+
+ interface IBldBundle {
+ /**
+ * gets bundle activator
+ */
+ String getActivator();
+
+ /**
+ * gets bundle id within project.
+ */
+ String getId();
+
+ /**
+ * gets bundle version.
+ */
+ String getVersion();
+
+ /**
+ * gets the Bundle-SymbolicName.
+ */
+ String getSymbolicName();
+
+ /**
+ * gets bundles export-packages.
+ */
+ List<IPackageExport> getExports();
+
+ /**
+ * gets project import-packages.
+ */
+ List<IPackageImport> getImports();
+
+ /**
+ * gets project require-bundles.
+ */
+ List<IRequiredBundle> getRequires();
+
+ /**
+ * get bundle fragment-host.
+ */
+ IRequiredBundle getFragmentHost();
+
+ /**
+ * gets bundle libs.
+ */
+ Map<String, Map<String, String>> getLibs();
+
+ /**
+ * gets the bundle contents
+ * @return list of package patterns.
+ */
+ List<String> getContents();
+
+ /**
+ * gets the bundle's associated dljar contents.
+ * This is a convenience which avoids having to define another bundle
+ * just for the dljar, which is then added to the parent bundle.
+ * @return list of package patterns.
+ */
+ List<String> getDownloadContents();
+
+ /**
+ * gets SCA composites.
+ */
+ List<String> getComposites();
+
+ /**
+ * gets the additional resources.
+ * @return map with key as path in bundle, value as path in file system.
+ * Paths are resolved relative to location of project file and also from classpath.
+ */
+ Map<String,String> getResources();
+
+ /**
+ * gets additional bundle headers.
+ */
+ Properties getHeaders();
+
+ /**
+ * resolves a relative path against the project file location.
+ */
+ File resolve(String path);
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java
new file mode 100644
index 0000000..edcc1d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/config/IRepositoryConfig.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.config;
+
+import java.util.Map;
+import java.util.Properties;
+
+public interface IRepositoryConfig {
+ static final String REPOSITORY_PROVIDER = "provider";
+ static final String REPOSITORY_LEVEL = "level";
+
+ /**
+ * get properties with which to instantiate repositories.
+ * The key REPOSITORY_PROVIDER will be set to the fully qualified class name of the IRepositoryProvider.
+ * The key REPOSITORY_LEVEL indicates repository search order.
+ * @return
+ */
+ Map<String,Properties> getRepositoryConfig();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java
new file mode 100644
index 0000000..c39e8ab
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/BldCore.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.cauldron.bld.core.internal.license.LicenseManager;
+import org.cauldron.bld.core.internal.model.eclipse.DownloadJar;
+import org.cauldron.bld.core.internal.model.eclipse.Library;
+import org.cauldron.bld.core.internal.model.eclipse.LibraryImport;
+import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
+import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
+import org.cauldron.bld.core.internal.model.osgi.PackageExport;
+import org.cauldron.bld.core.internal.model.osgi.PackageImport;
+import org.cauldron.bld.core.internal.model.osgi.RequiredBundle;
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.INewtonSystem;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class BldCore implements BundleActivator {
+ private static LicenseManager licenceManager = new LicenseManager();
+
+ private static final Logger log = Logger.getLogger(BldCore.class.getName());
+
+ public static void error(String string, Throwable e) {
+ // TODO
+ log.log( Level.WARNING, string, e );
+ }
+
+ public static void error(String string) {
+ log.log( Level.WARNING, string );
+ }
+
+ public static ILicenseManager getLicenseManager() {
+ return licenceManager;
+ }
+
+ public void start(BundleContext context) throws Exception {
+ init();
+ }
+
+ public static void init() throws Exception {
+ String uri = "http://sigil.codecauldron.org/xml/sigil-namespace";
+ ModelElementFactory.getInstance().register(ISigilBundle.class,
+ SigilBundle.class, "bundle", "sigil", uri);
+ ModelElementFactory.getInstance().register(IDownloadJar.class,
+ DownloadJar.class, "download", "sigil", uri);
+ ModelElementFactory.getInstance().register(ILibrary.class,
+ Library.class, "library", "sigil", uri);
+ ModelElementFactory.getInstance().register(ILibraryImport.class,
+ LibraryImport.class, "library-import", "sigil", uri);
+
+ // osgi elements
+ ModelElementFactory.getInstance().register(IBundleModelElement.class,
+ BundleModelElement.class, "bundle", null, null);
+ ModelElementFactory.getInstance().register(IPackageExport.class,
+ PackageExport.class, "package.export", null, null);
+ ModelElementFactory.getInstance().register(IPackageImport.class,
+ PackageImport.class, "package.import", null, null);
+ ModelElementFactory.getInstance().register(IRequiredBundle.class,
+ RequiredBundle.class, "required.bundle", null, null);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java
new file mode 100644
index 0000000..456cfc9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicenseManager.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.license;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+//import org.cauldron.sigil.model.project.ISigilProjectModel;
+
+public class LicenseManager implements ILicenseManager {
+
+ private HashMap<String, Pattern> licenses = new HashMap<String, Pattern>();
+ private HashMap<String, LicensePolicy> policies = new HashMap<String, LicensePolicy>();
+ private LicensePolicy defaultPolicy = new LicensePolicy(this);
+
+ public void addLicense(String name, Pattern pattern) {
+ licenses.put( name, pattern );
+ }
+
+ public void removeLicense(String name) {
+ licenses.remove(name);
+ }
+
+ public Set<String> getLicenseNames() {
+ return Collections.unmodifiableSet(licenses.keySet());
+ }
+
+ public Pattern getLicensePattern(String name) {
+ return licenses.get( name );
+ }
+
+ public ILicensePolicy getDefaultPolicy() {
+ return defaultPolicy;
+ }
+
+ //public ILicensePolicy getPolicy(ISigilProjectModel project) {
+ // synchronized( policies ) {
+ // LicensePolicy p = policies.get(project.getName());
+ //
+ // if ( p == null ) {
+ // p = new LicensePolicy(this, project);
+ // policies.put( project.getName(), p );
+ // }
+ //
+ // return p;
+ // }
+ //}
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java
new file mode 100644
index 0000000..4a00b00
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/license/LicensePolicy.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.license;
+
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class LicensePolicy implements ILicensePolicy {
+
+ private LicenseManager licenseManager;
+
+ public LicensePolicy(LicenseManager licenseManager) {
+ this.licenseManager = licenseManager;
+ }
+
+ public boolean accept(ISigilBundle bundle) {
+ return true;
+ }
+
+ public void addAllowed(String licenseName) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeAllowed(String licenseName) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void save(IProgressMonitor monitor) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java
new file mode 100644
index 0000000..f8bc392
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/DownloadJar.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.eclipse.core.runtime.IPath;
+
+public class DownloadJar extends AbstractCompoundModelElement implements IDownloadJar {
+
+ private static final long serialVersionUID = 1L;
+
+ private Set<IPath> entries = new HashSet<IPath>();
+
+ public DownloadJar() {
+ super("RMI Classpath Download Jar");
+ }
+
+ public void addEntry(IPath entry) {
+ entries.add( entry );
+ }
+
+ public void removeEntry(IPath entry) {
+ entries.remove( entry );
+ }
+
+ public Set<IPath> getEntrys() {
+ return entries;
+ }
+
+ public void clearEntries() {
+ entries.clear();
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java
new file mode 100644
index 0000000..9ce6be8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/Library.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public class Library extends AbstractCompoundModelElement implements ILibrary {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private Version version;
+ private Set<IRequiredBundle> bundles;
+ private Set<IPackageImport> imports;
+
+ public Library() {
+ super("Library");
+ bundles = new HashSet<IRequiredBundle>();
+ imports = new HashSet<IPackageImport>();
+ }
+
+ public void addBundle(IRequiredBundle bundle) {
+ bundles.add(bundle);
+ }
+
+ public void addImport(IPackageImport pi) {
+ imports.add(pi);
+ }
+
+ public Collection<IRequiredBundle> getBundles() {
+ return bundles;
+ }
+
+ public Collection<IPackageImport> getImports() {
+ return imports;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void removeBundle(IRequiredBundle bundle) {
+ bundles.remove(bundle);
+ }
+
+ public void removeImport(IPackageImport pi) {
+ imports.remove(pi);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java
new file mode 100644
index 0000000..2c86944
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/LibraryImport.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+
+public class LibraryImport extends AbstractModelElement implements ILibraryImport {
+
+ private static final long serialVersionUID = 1L;
+
+ public LibraryImport() {
+ super("Library Import");
+ }
+
+ private String libraryName;
+ private VersionRange range = VersionRange.ANY_VERSION;
+
+ public String getLibraryName() {
+ return libraryName;
+ }
+
+ public VersionRange getVersions() {
+ return range;
+ }
+
+ public void setLibraryName(String name) {
+ this.libraryName = name;
+ }
+
+ public void setVersions(VersionRange range) {
+ this.range = range;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java
new file mode 100644
index 0000000..1e09fcc
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/eclipse/SigilBundle.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.eclipse;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilBundle extends AbstractCompoundModelElement implements ISigilBundle {
+
+ private static final long serialVersionUID = 1L;
+
+ private IBundleModelElement bundle;
+ private IDownloadJar download;
+ private Set<IPath> sourcePaths;
+ private Set<IPath> libraryPaths;
+ private Set<ISCAComposite> composites;
+ private Set<String> classpath;
+ private Set<String> packages;
+ private Set<String> dlPackages;
+ private IPath location;
+
+ private IPath sourcePathLocation;
+ private IPath licencePathLocation;
+ private IPath sourceRootPath;
+ private String bundleHost;
+
+ public SigilBundle() {
+ super( "Sigil Bundle" );
+ sourcePaths = new HashSet<IPath>();
+ libraryPaths = new HashSet<IPath>();
+ composites = new HashSet<ISCAComposite>();
+ classpath = new HashSet<String>();
+ packages = new HashSet<String>();
+ dlPackages = new HashSet<String>();
+ }
+
+ public void synchronize(IProgressMonitor monitor) throws IOException {
+ SubMonitor progress = SubMonitor.convert(monitor, 100);
+ progress.subTask("Synchronizing " + bundle.getSymbolicName() + " binary" );
+ sync(location, bundle.getUpdateLocation(), progress.newChild(45));
+
+ try {
+ progress.subTask("Synchronizing " + bundle.getSymbolicName() + " source" );
+ sync(sourcePathLocation, bundle.getSourceLocation(), progress.newChild(45));
+ } catch (IOException e) {
+ BldCore.error( "Failed to download source for " + bundle.getSymbolicName() + " " + bundle.getVersion(), e.getCause() );
+ }
+
+ try {
+ progress.subTask("Synchronizing " + bundle.getSymbolicName() + " licence" );
+ sync(licencePathLocation, bundle.getLicenseURI(), progress.newChild(10));
+ } catch (IOException e) {
+ BldCore.error( "Failed to download licence for " + bundle.getSymbolicName() + " " + bundle.getVersion(), e.getCause() );
+ }
+ }
+
+ public boolean isSynchronized() {
+ return location == null || location.toFile().exists();
+ }
+
+ private static void sync(IPath local, URI remote, IProgressMonitor monitor) throws IOException {
+ try {
+ if ( local != null && !local.toFile().exists() ) {
+ if ( remote != null ) {
+ URL url = remote.toURL();
+ URLConnection connection = url.openConnection();
+ int contentLength = connection.getContentLength();
+
+ monitor.beginTask("Downloading from " + url.getHost(), contentLength);
+
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ URLConnection conn = url.openConnection();
+ if ( conn instanceof HttpURLConnection ) {
+ HttpURLConnection http = (HttpURLConnection) conn;
+ http.setConnectTimeout(10000);
+ http.setReadTimeout(5000);
+ }
+ in = conn.getInputStream();
+ File f = local.toFile();
+ f.getParentFile().mkdirs();
+ out = new FileOutputStream( f );
+ stream( in, out, monitor );
+ }
+ finally {
+ if ( in != null ) {
+ in.close();
+ }
+ if ( out != null ) {
+ out.close();
+ }
+ monitor.done();
+ }
+ }
+ }
+ }
+ catch (IOException e) {
+ local.toFile().delete();
+ throw e;
+ }
+ }
+
+ private static void stream(InputStream in, OutputStream out, IProgressMonitor monitor) throws IOException {
+ byte[] b = new byte[1024];
+ for ( ;; ) {
+ if ( monitor.isCanceled() ) {
+ throw new InterruptedIOException( "User canceled download" );
+ }
+ int r = in.read( b );
+ if ( r == -1 ) break;
+ out.write(b, 0, r);
+ monitor.worked(r);
+ }
+
+ out.flush();
+ }
+
+ public IBundleModelElement getBundleInfo() {
+ return bundle;
+ }
+
+ public void setBundleInfo(IBundleModelElement bundle) {
+ if ( bundle == null ) {
+ if (this.bundle != null) {
+ this.bundle.setParent(null);
+ }
+ }
+ else {
+ bundle.setParent(this);
+ }
+ this.bundle = bundle;
+ }
+
+ public IDownloadJar getDownloadJar() {
+ return download;
+ }
+
+ public void setDownloadJar(IDownloadJar download) {
+ this.download = download;
+ }
+
+ public void addLibraryPath( IPath path ) {
+ libraryPaths.add( path );
+ }
+
+ public void removeLibraryPath( IPath path ) {
+ libraryPaths.remove( path );
+ }
+
+ public Set<IPath> getLibraryPaths() {
+ return libraryPaths;
+ }
+
+ public void addSourcePath( IPath path ) {
+ sourcePaths.add( path );
+ }
+
+ public void removeSourcePath( IPath path ) {
+ sourcePaths.remove( path );
+ }
+
+ public Set<IPath> getSourcePaths() {
+ return sourcePaths;
+ }
+
+ public void clearSourcePaths() {
+ sourcePaths.clear();
+ }
+
+ public void addComposite(ISCAComposite composite) {
+ composites.add( composite );
+ composite.setParent(this);
+ }
+
+ public Set<ISCAComposite> getComposites() {
+ return composites;
+ }
+
+ public void removeComposite(ISCAComposite composite) {
+ if ( composites.remove( composite ) ) {
+ composite.setParent(null);
+ }
+ }
+ public void addClasspathEntry(String encodedClasspath) {
+ classpath.add( encodedClasspath.trim() );
+ }
+
+ public Set<String> getClasspathEntrys() {
+ return classpath;
+ }
+
+ public void removeClasspathEntry(String encodedClasspath) {
+ classpath.remove(encodedClasspath.trim());
+ }
+
+ public IPath getLocation() {
+ return location;
+ }
+
+ public void setLocation(IPath location) {
+ this.location = location;
+ }
+
+ public IPath getSourcePathLocation() {
+ return sourcePathLocation;
+ }
+ public IPath getSourceRootPath() {
+ return sourceRootPath;
+ }
+ public void setSourcePathLocation(IPath location) {
+ this.sourcePathLocation = location;
+ }
+ public void setSourceRootPath(IPath location) {
+ this.sourceRootPath = location;
+ }
+
+ @Override
+ public String toString() {
+ return "SigilBundle[" + (getBundleInfo() == null ? null : (getBundleInfo().getSymbolicName() + ":" + getBundleInfo().getVersion())) + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( obj == null ) return false;
+ if ( obj == this ) return true;
+
+ if ( obj instanceof SigilBundle) {
+ return obj.toString().equals( toString() );
+ }
+
+ return false;
+ }
+ @Override
+ public int hashCode() {
+ return 31 * toString().hashCode();
+ }
+
+ public IPath getLicencePathLocation() {
+ return licencePathLocation;
+ }
+
+ public void setLicencePathLocation(IPath licencePathLocation) {
+ this.licencePathLocation = licencePathLocation;
+ }
+
+ public String getFragmentHost() {
+ return bundleHost;
+ }
+
+ public void setFragmentHost(String symbolicName) {
+ this.bundleHost = symbolicName;
+ }
+
+ public String getElementName() {
+ return bundle.getSymbolicName();
+ }
+
+ public Version getVersion() {
+ return bundle.getVersion();
+ }
+
+ public void setVersion(Version version) {
+ this.bundle.setVersion(version);
+ }
+
+ public String getSymbolicName() {
+ return bundle.getSymbolicName();
+ }
+
+ public Set<String> getPackages() {
+ return packages;
+ }
+
+ public void addPackage(String pkg) {
+ packages.add(pkg);
+ }
+
+ public boolean removePackage(String pkg) {
+ return packages.remove(pkg);
+ }
+
+ public Set<String> getDownloadPackages() {
+ return dlPackages;
+ }
+
+ public void addDownloadPackage(String pkg) {
+ dlPackages.add(pkg);
+ }
+
+ public boolean removeDownloadPackage(String pkg) {
+ return dlPackages.remove(pkg);
+ }
+
+ public IPackageExport findExport(String packageName) {
+ for ( IPackageExport e : bundle.getExports() ) {
+ if ( packageName.equals( e.getPackageName() ) ) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ public IPackageImport findImport(String packageName) {
+ for ( IPackageImport i : bundle.getImports() ) {
+ if ( packageName.equals( i.getPackageName() ) ) {
+ return i;
+ }
+ }
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java
new file mode 100644
index 0000000..f4024ef
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/BundleModelElement.java
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.InvalidModelException;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public class BundleModelElement extends AbstractCompoundModelElement implements IBundleModelElement {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ // required obr values
+ private URI updateLocation;
+ private String symbolicName;
+ private Version version = Version.emptyVersion;
+ private Set<IPackageImport> imports;
+ private Set<IPackageExport> exports;
+ private Set<IRequiredBundle> requires;
+ private URI sourceLocation;
+ private Set<String> classpathElements;
+ private IRequiredBundle fragmentHost;
+
+ // human readable values
+ private String name;
+ private String description;
+ private String category;
+ private URI licenseURI;
+ private URI docURI;
+ private String vendor;
+ private String contactAddress;
+ private String copyright;
+
+ // internal values
+ private String activator;
+ private Set<ILibraryImport> libraries;
+
+ public BundleModelElement() {
+ super( "OSGi Bundle" );
+ this.imports = new HashSet<IPackageImport>();
+ this.exports = new HashSet<IPackageExport>();
+ this.requires = new HashSet<IRequiredBundle>();
+ this.classpathElements = new HashSet<String>();
+ this.libraries = new HashSet<ILibraryImport>();
+ }
+
+ public String getActivator() {
+ return activator;
+ }
+
+ public void setActivator(String activator) {
+ this.activator = activator;
+ }
+
+ public void addLibraryImport(ILibraryImport library) {
+ libraries.add(library);
+ }
+
+ public Set<ILibraryImport> getLibraryImports() {
+ return libraries;
+ }
+
+ public void removeLibraryImport(ILibraryImport library) {
+ libraries.remove(library);
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getCategory()
+ */
+ public String getCategory() {
+ return category;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setCategory(java.lang.String)
+ */
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getContactAddress()
+ */
+ public String getContactAddress() {
+ return contactAddress;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setContactAddress(java.lang.String)
+ */
+ public void setContactAddress(String contactAddress) {
+ this.contactAddress = contactAddress;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getCopyright()
+ */
+ public String getCopyright() {
+ return copyright;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setCopyright(java.lang.String)
+ */
+ public void setCopyright(String copyright) {
+ this.copyright = copyright;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getDocURI()
+ */
+ public URI getDocURI() {
+ return docURI;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setDocURI(java.net.URI)
+ */
+ public void setDocURI(URI docURI) {
+ this.docURI = docURI;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getExports()
+ */
+ public Set<IPackageExport> getExports() {
+ return exports;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addExport(org.cauldron.sigil.model.osgi.PackageExport)
+ */
+ public void addExport(IPackageExport packageExport) {
+ exports.add(packageExport);
+ packageExport.setParent(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeExport(org.cauldron.sigil.model.osgi.PackageExport)
+ */
+ public void removeExport(IPackageExport packageExport) {
+ if ( exports.remove(packageExport) ) {
+ packageExport.setParent(null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getImports()
+ */
+ public Set<IPackageImport> getImports() {
+ return imports;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addImport(org.cauldron.sigil.model.osgi.PackageImport)
+ */
+ public void addImport(IPackageImport packageImport) {
+ imports.add(packageImport);
+ packageImport.setParent(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeImport(org.cauldron.sigil.model.osgi.PackageImport)
+ */
+ public void removeImport(IPackageImport packageImport) {
+ if ( imports.remove( packageImport ) ) {
+ packageImport.setParent(null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getRequiredBundles()
+ */
+ public Set<IRequiredBundle> getRequiredBundles() {
+ return requires;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#addRequiredBundle(org.cauldron.sigil.model.osgi.RequiresBundle)
+ */
+ public void addRequiredBundle(IRequiredBundle bundle) {
+ requires.add( bundle );
+ bundle.setParent(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#removeRequiredBundle(org.cauldron.sigil.model.osgi.RequiresBundle)
+ */
+ public void removeRequiredBundle(IRequiredBundle bundle) {
+ if ( requires.remove(bundle) ) {
+ bundle.setParent(null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getLicenseURI()
+ */
+ public URI getLicenseURI() {
+ return licenseURI;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setLicenseURI(java.net.URI)
+ */
+ public void setLicenseURI(URI licenseURI) {
+ this.licenseURI = licenseURI;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getSourceLocation()
+ */
+ public URI getSourceLocation() {
+ return sourceLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setSourceLocation(java.net.URI)
+ */
+ public void setSourceLocation(URI sourceLocation) {
+ this.sourceLocation = sourceLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getSymbolicName()
+ */
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setSymbolicName(java.lang.String)
+ */
+ public void setSymbolicName(String symbolicName) {
+ this.symbolicName = symbolicName == null ? null : symbolicName.intern();
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getUpdateLocation()
+ */
+ public URI getUpdateLocation() {
+ return updateLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setUpdateLocation(java.net.URI)
+ */
+ public void setUpdateLocation(URI updateLocation) {
+ this.updateLocation = updateLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getVendor()
+ */
+ public String getVendor() {
+ return vendor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setVendor(java.lang.String)
+ */
+ public void setVendor(String vendor) {
+ this.vendor = vendor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#getVersion()
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.model.osgi.IBundleModelElement#setVersion(java.lang.String)
+ */
+ public void setVersion(Version version) {
+ this.version = version == null ? Version.emptyVersion : version;
+ }
+
+ public void checkValid() throws InvalidModelException {
+ if (symbolicName == null)
+ throw new InvalidModelException(this, "Bundle symbolic name not set");
+ }
+
+ public BundleModelElement clone() {
+ BundleModelElement bd = (BundleModelElement) super.clone();
+
+ bd.imports = new HashSet<IPackageImport>();
+ bd.exports = new HashSet<IPackageExport>();
+ bd.requires = new HashSet<IRequiredBundle>();
+
+ for (IPackageImport pi : imports ) {
+ bd.imports.add((IPackageImport) pi.clone());
+ }
+
+ for (IPackageExport pe : exports ) {
+ bd.exports.add((IPackageExport) pe.clone());
+ }
+
+ for ( IRequiredBundle rb : requires ) {
+ bd.requires.add((IRequiredBundle) rb.clone());
+ }
+
+ return bd;
+ }
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("BundleModelElement[");
+ buf.append(symbolicName);
+ buf.append(", ");
+ buf.append(version);
+ buf.append("]");
+
+ return buf.toString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void addClasspath(String path) {
+ classpathElements.add( path );
+ }
+
+ public Collection<String> getClasspaths() {
+ return classpathElements.isEmpty() ? Collections.singleton( "." ) : classpathElements;
+ }
+
+ public void removeClasspath(String path) {
+ classpathElements.remove( path );
+ }
+
+ public IRequiredBundle getFragmentHost() {
+ return fragmentHost;
+ }
+
+ public void setFragmentHost(IRequiredBundle fragmentHost) {
+ this.fragmentHost = fragmentHost;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java
new file mode 100644
index 0000000..536dbaf
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageExport.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.osgi.framework.Version;
+
+public class PackageExport extends AbstractModelElement implements IPackageExport {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private Version version;
+ private HashSet<String> uses = new HashSet<String>();
+
+ public PackageExport() {
+ super("OSGi Package Export");
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#getPackageName()
+ */
+ public String getPackageName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#setPackageName(java.lang.String)
+ */
+ public void setPackageName(String packageName) {
+ this.name = packageName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#getVersion()
+ */
+ public Version getVersion() {
+ Version result;
+ if(version != null) {
+ result = version;
+ } else {
+ ISigilBundle owningBundle = getAncestor(ISigilBundle.class);
+ if(owningBundle == null) {
+ result = Version.emptyVersion;
+ } else {
+ result = owningBundle.getVersion();
+ }
+ }
+ return result;
+ }
+
+ public Version getRawVersion() {
+ return version;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageExport#setVersion(java.lang.String)
+ */
+ public void setVersion(Version version) {
+ this.version = version; // == null ? Version.emptyVersion : version;
+ }
+
+ public void addUse(String use) {
+ uses.add(use);
+ }
+
+ public Collection<String> getUses() {
+ return uses;
+ }
+
+ public void removeUse(String use) {
+ uses.remove(use);
+ }
+
+ @Override
+ public String toString() {
+ return "PackageExport[" + name + ":" + version + ":uses=" + uses + "]";
+ }
+
+ public void setUses(Collection<String> uses) {
+ this.uses.clear();
+ this.uses.addAll(uses);
+ }
+
+ public int compareTo(IPackageExport o) {
+ int i = name.compareTo(o.getPackageName());
+
+ if ( i == 0 ) {
+ i = compareVersion(o.getVersion());
+ }
+
+ return i;
+ }
+
+ private int compareVersion(Version other) {
+ if ( version == null ) {
+ if ( other == null ) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+ }
+ else {
+ if ( other == null ) {
+ return -1;
+ }
+ else {
+ return version.compareTo(other);
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java
new file mode 100644
index 0000000..a9319f3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/PackageImport.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.InvalidModelException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class PackageImport extends AbstractModelElement implements IPackageImport {
+
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private VersionRange versions = VersionRange.ANY_VERSION;
+
+ // resolution directive
+ private boolean optional;
+ private boolean dependency = true;
+ private OSGiImport osgiImport = OSGiImport.AUTO;
+
+ public PackageImport() {
+ super( "OSGi Package Import" );
+ }
+
+ @Override
+ public void checkValid() throws InvalidModelException {
+ if ( name == null ) {
+ throw new InvalidModelException( this, "Package name must be set" );
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#isOptional()
+ */
+ public boolean isOptional() {
+ return optional;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setOptional(boolean)
+ */
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ public boolean isDependency() {
+ return dependency;
+ }
+
+ public void setDependency(boolean dependency) {
+ this.dependency = dependency;
+ }
+
+ public OSGiImport getOSGiImport() {
+ return osgiImport;
+ }
+
+ public void setOSGiImport(OSGiImport osgiHeader) {
+ this.osgiImport = osgiHeader;
+ }
+
+ public String getPackageName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setName(java.lang.String)
+ */
+ public void setPackageName(String name) {
+ this.name = name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#getVersion()
+ */
+ public VersionRange getVersions() {
+ return versions;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IPackageImport#setVersion(java.lang.String)
+ */
+ public void setVersions(VersionRange versions) {
+ this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+ }
+
+ @Override
+ public String toString() {
+ return "Package-Import[" + name + ":" + versions + ":" + (optional ? "optional" : "mandatory") + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+
+ if ( obj instanceof PackageImport ) {
+ PackageImport pi = (PackageImport) obj;
+ return name.equals( pi.name ) && versions.equals( pi.versions ) && optional == pi.optional;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = name.hashCode() * versions.hashCode();
+
+ if ( optional ) {
+ hc *= -1;
+ }
+
+ return hc;
+ }
+
+ public boolean accepts(IModelElement provider) {
+ if ( provider instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) provider;
+ return pe.getPackageName().equals( name ) && versions.contains( pe.getVersion() );
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int compareTo(IPackageImport o) {
+ int i = name.compareTo(o.getPackageName());
+
+ if ( i == 0 ) {
+ i = compareVersion(o.getVersions());
+ }
+
+ return i;
+ }
+
+ private int compareVersion(VersionRange range) {
+ if ( versions == null ) {
+ if ( range == null ) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+ }
+ else {
+ if ( range == null ) {
+ return -1;
+ }
+ else {
+ return versions.getCeiling().compareTo(range.getCeiling());
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java
new file mode 100644
index 0000000..35c3f7b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/internal/model/osgi/RequiredBundle.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.internal.model.osgi;
+
+import org.cauldron.sigil.model.AbstractModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class RequiredBundle extends AbstractModelElement implements IRequiredBundle {
+ private static final long serialVersionUID = 1L;
+
+ private String symbolicName;
+ private VersionRange versions = VersionRange.ANY_VERSION;
+ private boolean optional;
+
+ public RequiredBundle() {
+ super("OSGi Bundle Requirement");
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#getSymbolicName()
+ */
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#setSymbolicName(java.lang.String)
+ */
+ public void setSymbolicName(String symbolicName) {
+ this.symbolicName = symbolicName == null ? null : symbolicName.intern();
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#getVersion()
+ */
+ public VersionRange getVersions() {
+ return versions;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.model.osgi.IRequiresBundle#setVersion(java.lang.String)
+ */
+ public void setVersions(VersionRange versions) {
+ this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ @Override
+ public String toString() {
+ return "RequiredBundle[" + symbolicName + ":" + versions + ":" + (optional ? "optional" : "mandatory") + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+
+ if ( obj instanceof RequiredBundle ) {
+ RequiredBundle rb = (RequiredBundle) obj;
+ return symbolicName.equals( rb.symbolicName ) && versions.equals( rb.versions ) && optional == rb.optional;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = symbolicName.hashCode() * versions.hashCode();
+
+ if ( optional ) {
+ hc *= -1;
+ }
+
+ return hc;
+ }
+
+ public boolean accepts(IModelElement provider) {
+ if ( provider instanceof IBundleModelElement ) {
+ IBundleModelElement bndl = (IBundleModelElement) provider;
+ return symbolicName.equals( bndl.getSymbolicName() ) && versions.contains( bndl.getVersion() );
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int compareTo(IRequiredBundle o) {
+ int i = symbolicName.compareTo(o.getSymbolicName());
+
+ if ( i == 0 ) {
+ i = compareVersion(o.getVersions());
+ }
+
+ return i;
+ }
+
+ private int compareVersion(VersionRange range) {
+ if ( versions == null ) {
+ if ( range == null ) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+ }
+ else {
+ if ( range == null ) {
+ return -1;
+ }
+ else {
+ return versions.getCeiling().compareTo(range.getCeiling());
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java
new file mode 100644
index 0000000..9157288
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicenseManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.licence;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+//import org.cauldron.sigil.model.project.ISigilProjectModel;
+
+public interface ILicenseManager {
+ void addLicense(String name, Pattern pattern);
+ void removeLicense(String name);
+ Set<String> getLicenseNames();
+ Pattern getLicensePattern(String name);
+ ILicensePolicy getDefaultPolicy();
+ //ILicensePolicy getPolicy(ISigilProjectModel project);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java
new file mode 100644
index 0000000..a5bcdac
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/licence/ILicensePolicy.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.licence;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface ILicensePolicy {
+ void addAllowed(String licenseName);
+ void removeAllowed(String licenseName);
+ boolean accept(ISigilBundle bundle);
+ void save(IProgressMonitor monitor);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java
new file mode 100644
index 0000000..57d859a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/BundleResolver.java
@@ -0,0 +1,444 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.bld.core.BldCore;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.osgi.framework.Version;
+
+public class BundleResolver implements IBundleResolver {
+
+ private class BundleOrderComparator implements Comparator<ISigilBundle> {
+ private IModelElement requirement;
+
+ public BundleOrderComparator(IModelElement requirement) {
+ this.requirement = requirement;
+ }
+
+ public int compare(ISigilBundle o1, ISigilBundle o2) {
+ int c = compareVersions(o1, o2);
+
+ if ( c == 0 ) {
+ c = compareImports(o1, o2);
+ }
+
+ return c;
+ }
+
+ private int compareImports(ISigilBundle o1, ISigilBundle o2) {
+ int c1 = o1.getBundleInfo().getImports().size();
+ int c2 = o2.getBundleInfo().getImports().size();
+
+ if ( c1 < c2 ) {
+ return -1;
+ }
+ else if ( c2 > c1 ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ private int compareVersions(ISigilBundle o1, ISigilBundle o2) {
+ Version v1 = null;
+ Version v2 = null;
+ if ( requirement instanceof IPackageImport ) {
+ v1 = findExportVersion( (IPackageImport) requirement, o1 );
+ v2 = findExportVersion( (IPackageImport) requirement, o2 );
+ }
+ else if ( requirement instanceof IRequiredBundle ) {
+ v1 = o1.getBundleInfo().getVersion();
+ v2 = o1.getBundleInfo().getVersion();
+ }
+
+ if ( v1 == null ) {
+ if ( v2 == null ) {
+ return 0;
+ }
+ else {
+ return 1;
+ }
+ }
+ else {
+ if ( v2 == null ) {
+ return -1;
+ }
+ else {
+ return v2.compareTo(v1);
+ }
+ }
+ }
+
+ private Version findExportVersion(IPackageImport pi, ISigilBundle o1) {
+ for ( IPackageExport pe : o1.getBundleInfo().getExports() ) {
+ if ( pi.getPackageName().equals( pi.getPackageName() ) ) {
+ return pe.getVersion();
+ }
+ }
+
+ return null;
+ }
+
+ }
+
+ private class ResolutionContext {
+ private final IModelElement root;
+ private final ResolutionConfig config;
+ private final IResolutionMonitor monitor;
+
+ private final Resolution resolution = new Resolution();
+ private final Set<IModelElement> parsed = new HashSet<IModelElement>();
+ private final LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();
+
+ public ResolutionContext(IModelElement root, ResolutionConfig config, IResolutionMonitor monitor) {
+ this.root = root;
+ this.config = config;
+ this.monitor = monitor;
+ }
+
+ public void enterModelElement(IModelElement element) {
+ parsed.add(element);
+ }
+
+ public void exitModelElement(IModelElement element) {
+ parsed.remove(element);
+ }
+
+ public boolean isNewModelElement(IModelElement element) {
+ return !parsed.contains(element);
+ }
+
+ public boolean isValid() {
+ return resolution.isSuccess();
+ }
+
+ public void setValid(boolean valid) {
+ resolution.setSuccess(valid);
+ }
+
+ public ResolutionException newResolutionException() {
+ return new ResolutionException(root, requirements.toArray( new IModelElement[requirements.size()]) );
+ }
+
+ public void startRequirement(IModelElement element) {
+ requirements.add(element);
+ monitor.startResolution(element);
+ }
+
+ public void endRequirement(IModelElement element) {
+ ISigilBundle provider = resolution.getProvider(element);
+
+ setValid( provider != null || isOptional(element) || config.isIgnoreErrors() );
+
+ if ( isValid() ) {
+ // only clear stack if valid
+ // else use it as an aid to trace errors
+ requirements.remove(element);
+ }
+
+ monitor.endResolution( element, provider );
+ }
+ }
+
+ private class Resolution implements IResolution {
+ private Map<ISigilBundle, List<IModelElement>> providees = new HashMap<ISigilBundle, List<IModelElement>>();
+ private Map<IModelElement, ISigilBundle> providers = new HashMap<IModelElement, ISigilBundle>();
+ private boolean success = true; // assume success
+
+ boolean addProvider(IModelElement element, ISigilBundle provider) {
+ providers.put( element, provider );
+
+ List<IModelElement> requirements = providees.get( provider );
+
+ boolean isNewProvider = requirements == null;
+
+ if ( isNewProvider ) {
+ requirements = new ArrayList<IModelElement>();
+ providees.put( provider, requirements );
+ }
+
+ requirements.add( element );
+
+ return isNewProvider;
+ }
+
+ void removeProvider(IModelElement element, ISigilBundle provider) {
+ providers.remove(element);
+ List<IModelElement> e = providees.get(provider);
+ e.remove(element);
+ if ( e.isEmpty() ) {
+ providees.remove(provider);
+ }
+ }
+
+ void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public ISigilBundle getProvider(IModelElement requirement) {
+ return providers.get(requirement);
+ }
+
+ public Set<ISigilBundle> getBundles() {
+ return providees.keySet();
+ }
+
+ public List<IModelElement> getMatchedRequirements(ISigilBundle bundle) {
+ return providees.get(bundle);
+ }
+
+ public boolean isSynchronized() {
+ for ( ISigilBundle b : getBundles() ) {
+ if ( !b.isSynchronized() ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void synchronize(IProgressMonitor monitor) {
+ Set<ISigilBundle> bundles = getBundles();
+ SubMonitor progress = SubMonitor.convert(monitor, bundles.size());
+
+ for ( ISigilBundle b : bundles ) {
+ if ( monitor.isCanceled() ) {
+ break;
+ }
+
+ try {
+ b.synchronize(progress.newChild(1));
+ } catch (IOException e) {
+ BldCore.error( "Failed to synchronize " + b, e );
+ }
+ }
+ }
+ }
+
+ private static final IResolutionMonitor NULL_MONITOR = new IResolutionMonitor() {
+ public void endResolution(IModelElement requirement,
+ ISigilBundle sigilBundle) {
+ }
+
+ public boolean isCanceled() {
+ return false;
+ }
+
+ public void startResolution(IModelElement requirement) {
+ }
+ };
+
+ private IRepositoryManager repositoryManager;
+
+ public BundleResolver(IRepositoryManager repositoryManager) {
+ this.repositoryManager = repositoryManager;
+ }
+
+ public IResolution resolve(IModelElement element, ResolutionConfig config, IResolutionMonitor monitor) throws ResolutionException {
+ if ( monitor == null ) {
+ monitor = NULL_MONITOR;
+ }
+ ResolutionContext ctx = new ResolutionContext(element, config, monitor);
+
+ resolveElement(element, ctx);
+
+ if ( !ctx.isValid() ) {
+ throw ctx.newResolutionException();
+ }
+
+ return ctx.resolution;
+ }
+
+ private void resolveElement(IModelElement element, ResolutionContext ctx) throws ResolutionException {
+ if ( isRequirement(element) ) {
+ resolveRequirement(element, ctx);
+ }
+
+ if ( ctx.isValid() && element instanceof ICompoundModelElement ) {
+ resolveCompound((ICompoundModelElement) element, ctx);
+ }
+ }
+
+ private void resolveCompound(ICompoundModelElement compound, ResolutionContext ctx) throws ResolutionException {
+ for ( IModelElement element : compound.children() ) {
+ if ( ctx.isNewModelElement(element) ) {
+ if ( isRequirement(element) ) {
+ resolveRequirement(element, ctx);
+ }
+ else if ( element instanceof ICompoundModelElement ) {
+ if ( !ctx.monitor.isCanceled() ) {
+ ctx.enterModelElement( element );
+ resolveElement((ICompoundModelElement) element, ctx);
+ ctx.exitModelElement(element);
+ }
+ }
+
+ if ( !ctx.isValid() ) {
+ break;
+ }
+ }
+ }
+ }
+
+ private void resolveRequirement(IModelElement requirement, ResolutionContext ctx) throws ResolutionException {
+ if ( ctx.config.isOptional() || !isOptional(requirement) ) {
+ ctx.startRequirement(requirement );
+
+ try {
+ int[] priorities = repositoryManager.getPriorityLevels();
+
+ outer: for ( int i = 0; i< priorities.length; i++ ) {
+ List<ISigilBundle> providers = findProvidersAtPriority(priorities[i], requirement, ctx);
+
+ if ( !providers.isEmpty() && !ctx.monitor.isCanceled() ) {
+ if ( providers.size() > 1 ) {
+ Collections.sort(providers, new BundleOrderComparator(requirement));
+ }
+
+ for ( ISigilBundle provider : providers ) {
+ // reset validity - if there's another provider it can still be solved
+ ctx.setValid(true);
+ if ( ctx.resolution.addProvider(requirement, provider) ) {
+ if ( ctx.config.isDependents() ) {
+ resolveElement(provider, ctx);
+ }
+
+ if ( ctx.isValid() ) {
+ break outer;
+ }
+ else {
+ ctx.resolution.removeProvider(requirement, provider);
+ }
+ }
+ else {
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ finally {
+ ctx.endRequirement(requirement);
+ }
+ }
+ }
+
+ private List<ISigilBundle> findProvidersAtPriority(int i, IModelElement requirement, ResolutionContext ctx) throws ResolutionException {
+ ArrayList<ISigilBundle> providers = new ArrayList<ISigilBundle>();
+
+ for (IBundleRepository rep : repositoryManager.getRepositories(i)) {
+ if ( ctx.monitor.isCanceled() ) {
+ break;
+ }
+ providers.addAll( findProviders( requirement, ctx.config, rep ) );
+ }
+
+ return providers;
+ }
+
+ private Collection<ISigilBundle> findProviders(IModelElement requirement, ResolutionConfig config, IBundleRepository rep) throws ResolutionException {
+ ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ if ( requirement instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) requirement;
+ found.addAll( rep.findAllProviders( pi, config.getOptions() ) );
+ }
+ else if ( requirement instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) requirement;
+ found.addAll( rep.findAllProviders( rb, config.getOptions() ) );
+ }
+ else if ( requirement instanceof ILibraryImport ) {
+ ILibrary lib = repositoryManager.resolveLibrary((ILibraryImport) requirement);
+ if (lib != null) {
+ found.addAll( rep.findProviders(lib, config.getOptions()) );
+ }
+ }
+ else {
+ // shouldn't get here - developer error if do
+ // use isRequirement before getting anywhere near this logic...
+ throw new IllegalStateException( "Invalid requirement type " + requirement );
+ }
+
+ return found;
+ }
+
+
+ private boolean isOptional(IModelElement element) {
+ if ( element instanceof IPackageImport ) {
+ return ((IPackageImport) element).isOptional();
+ }
+ else if ( element instanceof IRequiredBundle ) {
+ return ((IRequiredBundle) element).isOptional();
+ }
+ else if ( element instanceof ILibraryImport ) {
+ ILibrary lib = repositoryManager.resolveLibrary((ILibraryImport) element);
+ for ( IPackageImport pi : lib.getImports() ) {
+ if ( !isOptional(pi) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ else {
+ // should never get this due to isRequirement test prior to calling this
+ // developer error if found
+ throw new IllegalStateException( "Invalid optional element test for " + element);
+ }
+ }
+
+ private boolean isRequirement(IModelElement element) {
+ return element instanceof IPackageImport || element instanceof IRequiredBundle || element instanceof ILibraryImport;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java
new file mode 100644
index 0000000..0bb50d5
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/DirectoryHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class DirectoryHelper {
+ public static void scanBundles(AbstractBundleRepository repository, List<ISigilBundle> bundles, IPath path, IPath source, boolean recursive) {
+ File dir = path.toFile();
+
+ if ( dir.exists() ) {
+ for ( File f : dir.listFiles() ){
+ if ( f.isDirectory() ) {
+ if ( recursive ) {
+ scanBundles( repository, bundles, new Path( f.getAbsolutePath() ), source, recursive );
+ }
+ }
+ else if ( f.isFile() && f.getName().endsWith( ".jar" )){
+ JarFile jar = null;
+ try {
+ jar = new JarFile(f);
+ ISigilBundle bundle = buildBundle(repository, jar.getManifest(), f );
+ if ( bundle != null ) {
+ bundle.setSourcePathLocation( source );
+ bundle.setSourceRootPath( new Path( "src" ) );
+ bundles.add( bundle );
+ }
+ } catch (IOException e) {
+ BldCore.error( "Failed to read jar file " + f, e );
+ } catch (ModelElementFactoryException e) {
+ BldCore.error( "Failed to build bundle " + f , e );
+ } catch (RuntimeException e) {
+ BldCore.error( "Failed to build bundle " + f , e );
+ }
+ finally {
+ if ( jar != null ) {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ BldCore.error( "Failed to close jar file", e );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static ISigilBundle buildBundle(
+ AbstractBundleRepository repository, Manifest manifest, File f) {
+ IBundleModelElement info = repository.buildBundleModelElement( manifest );
+
+ ISigilBundle bundle = null;
+
+ if ( info != null ) {
+ bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+ bundle.addChild(info);
+ bundle.setLocation( new Path( f.getAbsolutePath() ) );
+ }
+
+ return bundle;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java
new file mode 100644
index 0000000..3525a4e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepository.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+
+public class FileSystemRepository extends AbstractBundleRepository {
+
+ private ArrayList<ISigilBundle> bundles;
+ private IPath path;
+ private boolean recurse;
+
+ public FileSystemRepository(String id, IPath path, boolean recurse) {
+ super(id);
+ this.path = path;
+ this.recurse = recurse;
+ }
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ synchronized( this ) {
+ if ( bundles == null ) {
+ bundles = new ArrayList<ISigilBundle>();
+ DirectoryHelper.scanBundles(this, bundles, path, null, recurse);
+ }
+ }
+
+ for ( ISigilBundle b : bundles ) {
+ if ( !visitor.visit(b) ) {
+ break;
+ }
+ }
+ }
+
+ public void refresh() {
+ synchronized( this ) {
+ bundles = null;
+ }
+
+ notifyChange();
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java
new file mode 100644
index 0000000..093d06b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/FileSystemRepositoryProvider.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.Path;
+
+public class FileSystemRepositoryProvider implements IRepositoryProvider {
+
+ public IBundleRepository createRepository(String id, Properties preferences)
+ throws RepositoryException {
+ String dir = preferences.getProperty("dir");
+ if (!new File(dir).isDirectory()) {
+ throw new RepositoryException("directory '" + dir +"' does not exist.");
+ }
+ boolean recurse = Boolean.valueOf(preferences.getProperty("recurse"));
+ return new FileSystemRepository(id, new Path(dir), recurse);
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java
new file mode 100644
index 0000000..5ca4ec3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/ProgressWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+
+public class ProgressWrapper implements IProgressMonitor {
+
+ private IResolutionMonitor monitor;
+
+ public ProgressWrapper(IResolutionMonitor monitor) {
+ this.monitor = monitor;
+ }
+
+ public boolean isCanceled() {
+ return monitor.isCanceled();
+ }
+
+ public void beginTask(String name, int totalWork) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void done() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void internalWorked(double work) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setCanceled(boolean value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setTaskName(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void subTask(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void worked(int work) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java
new file mode 100644
index 0000000..b23b64b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepository.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.util.jar.JarFile;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+
+public class SystemRepository extends AbstractBundleRepository {
+
+ private final String packages;
+ private final IPath frameworkPath;
+
+ public SystemRepository(String id, IPath frameworkPath, String packages) {
+ super(id);
+ this.frameworkPath = frameworkPath;
+ this.packages = packages;
+ }
+
+ private static ISigilBundle systemBundle;
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ ISigilBundle bundle = loadSystemBundle();
+
+ if ( bundle != null ) {
+ visitor.visit(bundle);
+ }
+ }
+
+ private synchronized ISigilBundle loadSystemBundle() {
+ if (systemBundle == null) {
+ systemBundle = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+
+ JarFile jar = null;
+
+ try {
+ final IBundleModelElement info;
+ if (frameworkPath != null) {
+ systemBundle.setLocation(frameworkPath);
+ jar = new JarFile(frameworkPath.toFile());
+ info = buildBundleModelElement(jar.getManifest());
+ } else {
+ info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class);
+ }
+
+ applyProfile(info);
+ systemBundle.addChild(info);
+ } catch (IOException e) {
+ BldCore.error( "Failed to read jar file " + frameworkPath, e );
+ } catch (ModelElementFactoryException e) {
+ BldCore.error( "Failed to build bundle " + frameworkPath , e );
+ } catch (RuntimeException e) {
+ BldCore.error( "Failed to build bundle " + frameworkPath , e );
+ }
+ finally {
+ if (jar != null) {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ BldCore.error( "Failed to close jar file", e );
+ }
+ }
+ }
+ }
+
+ return systemBundle;
+ }
+
+ private void applyProfile(IBundleModelElement info) {
+ if (packages != null) {
+ for (String name : packages.split(",\\s*")) {
+ IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pe.setPackageName(name);
+ info.addExport(pe);
+ }
+ }
+ }
+
+ public synchronized void refresh() {
+ systemBundle = null;
+ notifyChange();
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java
new file mode 100644
index 0000000..c13e278
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/repository/SystemRepositoryProvider.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.Path;
+
+public class SystemRepositoryProvider implements IRepositoryProvider {
+
+ public IBundleRepository createRepository(String id, Properties properties)
+ throws RepositoryException {
+ String fw = properties.getProperty("framework");
+ Path frameworkPath = fw == null ? null : new Path(fw);
+ String extraPkgs = properties.getProperty("packages");
+ String profile = properties.getProperty("profile");
+
+ try {
+ Properties p = readProfile(profile);
+ String pkgs = p.getProperty("org.osgi.framework.system.packages") + "," + extraPkgs;
+ return new SystemRepository(id, frameworkPath, pkgs);
+ } catch (IOException e) {
+ throw new RepositoryException("Failed to load profile", e);
+ }
+ }
+
+ public static Properties readProfile(String name) throws IOException {
+ if (name == null) {
+ String version = System.getProperty("java.specification.version");
+ String[] split = version.split("\\.");
+ String prefix = ("6".compareTo(split[1]) <= 0) ? "JavaSE-" : "J2SE-";
+ name = prefix + version;
+ }
+
+ String profilePath = "profiles/" + name + ".profile";
+ InputStream in = SystemRepositoryProvider.class.getClassLoader().getResourceAsStream(profilePath);
+
+ if (in == null)
+ throw new IOException("No such profile: " + profilePath);
+
+ Properties p = new Properties();
+ p.load(in);
+
+ return p;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java
new file mode 100644
index 0000000..29c22d1
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/bld/core/util/QuoteUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.core.util;
+
+import java.util.ArrayList;
+
+public class QuoteUtil {
+ public static String[] split(String str) {
+ ArrayList<String> split = new ArrayList<String>();
+ boolean quote = false;
+ StringBuffer buf = new StringBuffer(str.length());
+
+ for ( int i = 0; i < str.length(); i++ ) {
+ char c = str.charAt(i);
+ switch ( c ) {
+ case '"':
+ quote = !quote;
+ break;
+ case ',':
+ if ( !quote ) {
+ split.add( buf.toString().trim() );
+ buf.setLength(0);
+ break;
+ }
+ // else fall through on purpose
+ default:
+ buf.append( c );
+ }
+ }
+
+ if ( buf.length() > 0 ) {
+ split.add( buf.toString().trim() );
+ }
+ return split.toArray( new String[split.size()] );
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java
new file mode 100644
index 0000000..98b1a22
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractCompoundModelElement.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+public abstract class AbstractCompoundModelElement extends AbstractModelElement implements ICompoundModelElement {
+
+ private static final long serialVersionUID = 1L;
+
+ public AbstractCompoundModelElement(String description) {
+ super(description);
+ }
+
+ public boolean addChild(IModelElement child) throws InvalidModelException {
+ return support.addChild(child);
+ }
+
+ public boolean removeChild(IModelElement child) {
+ return support.removeChild(child);
+ }
+
+ public IModelElement[] children() {
+ return support.children();
+ }
+
+ private static final ThreadLocal<Map<IModelWalker,Set<IModelElement>>> walkedLocal = new ThreadLocal<Map<IModelWalker,Set<IModelElement>>>();
+
+ public void visit(IModelWalker walker) {
+ if ( walker.visit( this ) ) {
+ Map<IModelWalker,Set<IModelElement>> walked = walkedLocal.get();
+ boolean delete = false;
+
+ if ( walked == null ) {
+ walked = new HashMap<IModelWalker, Set<IModelElement>>();
+ walkedLocal.set(walked);
+ }
+
+ Set<IModelElement> check = walked.get(walker);
+
+ if ( check == null ) {
+ delete = true;
+ check = new HashSet<IModelElement>();
+ }
+
+ check.add( this );
+
+ try {
+ for ( IModelElement e : children() ) {
+ if ( !check.contains( e ) && walker.visit( e ) ) {
+ check.add( e );
+ if ( e instanceof ICompoundModelElement ) {
+ ICompoundModelElement c = (ICompoundModelElement) e;
+ c.visit(walker);
+ }
+ }
+ }
+ }
+ finally {
+ if ( delete ) {
+ walked.remove(walker);
+
+ if ( walked.isEmpty() ) {
+ walkedLocal.set( null );
+ }
+ }
+ }
+ }
+ }
+
+ public Set<Class<? extends IModelElement>> getOptionalChildren() {
+ return support.getChildrenTypes(false);
+ }
+
+ public Set<Class<? extends IModelElement>> getRequiredChildren() {
+ return support.getChildrenTypes(true);
+ }
+
+ public <T extends IModelElement> T[] childrenOfType(Class<T> type) {
+ return support.childrenOfType( type );
+ }
+
+
+ @Override
+ public void checkValid() throws InvalidModelException {
+ super.checkValid();
+
+ for ( IModelElement e : support.children() ) {
+ e.checkValid();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java
new file mode 100644
index 0000000..e65b3ea
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/AbstractModelElement.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+public abstract class AbstractModelElement implements IModelElement {
+
+ private static final long serialVersionUID = 1L;
+
+ private IModelElement parent;
+
+ private String description;
+ private transient Map<Object, Object> meta;
+ private Map<Serializable, Serializable> serializedMeta;
+ private OverrideOptions override;
+
+ protected final ModelElementSupport support;
+
+ public AbstractModelElement(String description) {
+ support = new ModelElementSupport(this);
+ this.description = description.intern();
+ this.meta = new HashMap<Object, Object>();
+ this.serializedMeta = new HashMap<Serializable, Serializable>();
+ }
+
+ public String getElementDescription() {
+ return description;
+ }
+
+ public Map<Object, Object> getMeta() {
+ return meta;
+ }
+
+ public void setMeta(Map<Object, Object> meta) {
+ this.meta = meta;
+ }
+
+ @Override
+ public AbstractModelElement clone() {
+ try {
+ AbstractModelElement clone = (AbstractModelElement) super.clone();
+
+ clone.meta = new HashMap<Object, Object>(meta);
+
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ // can't happen but make compiler happy
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends IModelElement> T getAncestor(Class<T> type) {
+ IModelElement parent = this.parent;
+
+ while ( parent != null ) {
+ if ( type.isInstance( parent ) ) {
+ return (T) parent;
+ }
+ parent = parent.getParent();
+ }
+
+ return null;
+ }
+
+ public IModelElement getParent() {
+ return parent;
+ }
+
+ public void setParent( IModelElement parent ) {
+ if ( parent != null ) {
+ if ( this.parent != null && this.parent != parent ) {
+ throw new IllegalStateException( "Parent already installed");
+ }
+ }
+
+ this.parent = parent;
+ }
+
+ public void checkValid() throws InvalidModelException {
+ for ( String req : getRequiredProperties() ) {
+ try {
+ if ( getProperty( req ) == null ) {
+ throw new InvalidModelException(this, "Missing property " + req );
+ }
+ } catch (NoSuchMethodException e) {
+ throw new InvalidModelException( this, "No such property " + req );
+ }
+ }
+ }
+
+ public Object getProperty(String name) throws NoSuchMethodException {
+ return support.getProperty(name);
+ }
+
+ public void setProperty(String name, Object value)
+ throws NoSuchMethodException {
+ support.setProperty(name, value);
+ }
+
+ public void addProperty(String name, Object value)
+ throws NoSuchMethodException {
+ support.addProperty(name, value);
+ }
+
+ public void removeProperty(String name, Object value)
+ throws NoSuchMethodException {
+ support.removeProperty(name, value);
+ }
+
+ public Object getDefaultPropertyValue(String name) {
+ return support.getDefaultPropertyValue( name );
+ }
+
+ public Set<String> getPropertyNames() {
+ return support.getPropertyNames();
+ }
+
+ public Set<String> getRequiredProperties() {
+ return Collections.emptySet();
+ }
+
+ protected Object writeReplace() {
+ AbstractModelElement clone = clone();
+
+ for (Map.Entry<Object, Object> e : clone.meta.entrySet()) {
+ if (e.getKey() instanceof Serializable && e.getValue() instanceof Serializable) {
+ serializedMeta.put((Serializable) e.getKey(), (Serializable) e.getValue());
+ }
+ }
+
+ clone.meta.clear();
+
+ return clone;
+ }
+
+ public Class<?> getPropertyType(String name) throws NoSuchMethodException {
+ return support.getPropertyType(name);
+ }
+
+ protected Object readResolve() {
+ this.meta = new HashMap<Object, Object>(serializedMeta);
+ serializedMeta.clear();
+ return this;
+ }
+
+ public OverrideOptions getOverride() {
+ return override;
+ }
+
+ public void setOverride(OverrideOptions override) {
+ this.override = override;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java
new file mode 100644
index 0000000..8df2b11
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ICompoundModelElement.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.Set;
+
+public interface ICompoundModelElement extends IModelElement {
+ boolean addChild(IModelElement children) throws InvalidModelException;
+
+ boolean removeChild(IModelElement children);
+
+ IModelElement[] children();
+
+ void visit(IModelWalker walker);
+
+ <T extends IModelElement> T[] childrenOfType( Class<T> type );
+
+ Set<Class<? extends IModelElement>> getRequiredChildren();
+
+ Set<Class<? extends IModelElement>> getOptionalChildren();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java
new file mode 100644
index 0000000..0047bb9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependency.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependency extends IModelElement {
+ IDependent getDependent();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java
new file mode 100644
index 0000000..d97722e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependent.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependent extends IModelElement {
+ IDependency getDepender();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java
new file mode 100644
index 0000000..4b3c53e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IDependentModelElement.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IDependentModelElement extends IModelElement {
+ /**
+ * @return
+ */
+ IDependency[] getDependencies();
+
+ /**
+ * @param dependency
+ */
+ void addDependency(IModelElement dependency);
+
+ /**
+ * @param dependency
+ */
+ void removeDependency(IModelElement dependency);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java
new file mode 100644
index 0000000..4d230f0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelElement.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Descriptors represent static information about component, composite or system. They allow other services to decide
+ * how to deal with the given entity without the need to directly interact with the entity.
+ *
+ * @author dave
+ *
+ */
+public interface IModelElement extends Cloneable {
+ /**
+ * A brief human readable description of the component, composite or system.
+ *
+ * @return
+ */
+ String getElementDescription();
+
+ /**
+ * A set of key value pairs designed for use by a machine to classify a particular component, composite or system.
+ *
+ * @return
+ */
+ Map<Object, Object> getMeta();
+
+ /**
+ * Set meta data on this descriptor. Meta data is designed for use by a machine to classify or further enhance a
+ * particular component, composite or system.
+ *
+ * @param meta
+ */
+ void setMeta(Map<Object, Object> meta);
+
+ /**
+ * Check to see if this descriptor defines a complete set of properties. The definition of what constitutes a
+ * complete set is up to the implementing class.
+ *
+ * @throws InvalidModelException
+ */
+ void checkValid() throws InvalidModelException;
+
+ /**
+ * Find the parent element of this model element or null if no parent exists.
+ * @return
+ */
+ IModelElement getParent();
+
+ void setParent( IModelElement parent );
+
+ /**
+ * Finds the first ancestor up the hierarch of parents which is an instance of the specified
+ * type.
+ *
+ * @param type
+ * @return
+ */
+ <T extends IModelElement> T getAncestor(Class<T> type);
+
+ IModelElement clone();
+
+ Set<String> getPropertyNames();
+
+ void setProperty(String name, Object value) throws NoSuchMethodException;
+
+ void addProperty(String name, Object value) throws NoSuchMethodException;
+
+ void removeProperty(String name, Object value) throws NoSuchMethodException;
+
+ Object getProperty(String name) throws NoSuchMethodException;
+
+ Object getDefaultPropertyValue(String name);
+
+ Set<String> getRequiredProperties();
+
+ Class<?> getPropertyType(String name) throws NoSuchMethodException;
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java
new file mode 100644
index 0000000..b4525f8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelInfo.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IModelInfo {
+ String getGroupName();
+ String getGroupURI();
+ String getName();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java
new file mode 100644
index 0000000..a42c468
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IModelWalker.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+
+public interface IModelWalker {
+ boolean visit( IModelElement element );
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java
new file mode 100644
index 0000000..5a280db
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/INamedModelElement.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface INamedModelElement extends IModelElement {
+ void setName(String name);
+ String getName();
+ OverrideOptions getOverride();
+ void setOverride(OverrideOptions override);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java
new file mode 100644
index 0000000..9e9594c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/IRequirementModelElement.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public interface IRequirementModelElement {
+ boolean accepts(IModelElement provider);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java
new file mode 100644
index 0000000..d4751c0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/InvalidModelException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+/**
+ * @author dave
+ *
+ */
+public class InvalidModelException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ private IModelElement target;
+
+ public InvalidModelException(IModelElement target, String msg) {
+ super(msg);
+ this.target = target;
+ }
+
+ public InvalidModelException(IModelElement target, String msg, Throwable t) {
+ super(msg, t);
+ this.target = target;
+ }
+
+ public IModelElement getTarget() {
+ return target;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java
new file mode 100644
index 0000000..186d2d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactory.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ModelElementFactory {
+ static class ElementInfo {
+ Class<? extends IModelElement> implType;
+ String name;
+ String groupName;
+ String groupURI;
+
+ public ElementInfo(Class<? extends IModelElement> implType, String name, String groupName, String groupURI) {
+ this.implType = implType;
+ this.name = name;
+ this.groupName = groupName;
+ this.groupURI = groupURI;
+ }
+
+ public Class<? extends IModelElement> getImplType() {
+ return implType;
+ }
+ public String getName() {
+ return name;
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public String getGroupURI() {
+ return groupURI;
+ }
+
+ public String toString() {
+ return "ElementInfo[" + name + ":" + groupName + ":" + groupURI + ":" + implType.getCanonicalName() + "]";
+ }
+ }
+
+ static class ModelInfo implements IModelInfo {
+
+ private ElementInfo e;
+
+ public ModelInfo(ElementInfo e) {
+ this.e = e;
+ }
+
+ public String getGroupName() {
+ return e.getGroupName();
+ }
+
+ public String getGroupURI() {
+ return e.getGroupURI();
+ }
+
+ public String getName() {
+ return e.getName();
+ }
+
+ }
+
+ static class DefaultModelElementFactory extends ModelElementFactory {
+ private HashMap<Class<? extends IModelElement>, ElementInfo> elementInfo = new HashMap<Class<? extends IModelElement>, ElementInfo>();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends IModelElement> T newModelElement( Class<T> type ) throws ModelElementFactoryException {
+ ElementInfo info = elementInfo.get( type );
+ if ( info == null ) {
+ throw new ModelElementFactoryException( "No implementation registered for " + type );
+ }
+ try {
+ return (T) info.getImplType().newInstance();
+ } catch (InstantiationException e) {
+ throw new ModelElementFactoryException(e);
+ } catch (IllegalAccessException e) {
+ throw new ModelElementFactoryException(e);
+ }
+ }
+
+ @Override
+ public <T extends IModelElement> void register(Class<T> type, Class<? extends T> impl, String name, String groupName, String groupURI ) {
+ elementInfo.put( type, new ElementInfo( impl, name, groupName, groupURI ) );
+ }
+
+ @Override
+ public <T extends IModelElement> void unregister(Class<T> type,
+ Class<? extends T> impl) {
+ ElementInfo info = elementInfo.get( type );
+ if ( info != null && info.getImplType() == impl ) {
+ elementInfo.remove(type);
+ }
+ }
+
+ @Override
+ public IModelInfo getModelInfo(Class<? extends IModelElement> type) {
+ ElementInfo e = findElementInfo( type );
+
+ if ( e == null ) {
+ return null;
+ }
+
+ return new ModelInfo( e );
+ }
+
+ @Override
+ public IModelElement newModelElement(String namespaceURI, String localPart) throws ModelElementFactoryException {
+ for ( Map.Entry<Class<? extends IModelElement>, ElementInfo> e : elementInfo.entrySet() ) {
+ ElementInfo i = e.getValue();
+ if ( equal( namespaceURI, i.getGroupURI() ) && equal( i.getName(), localPart ) ) {
+ return newModelElement(e.getKey());
+ }
+ }
+
+ return null;
+ }
+
+ private boolean equal(String val1, String val2) {
+ return val1 == null ? val2 == null : val1.equals( val2 );
+ }
+
+ private ElementInfo findElementInfo( Class<? extends IModelElement> type ) {
+ for ( ElementInfo e : elementInfo.values() ) {
+ if ( e.getImplType() == type ) {
+ return e;
+ }
+ }
+
+ return null;
+ }
+
+ }
+
+ public abstract <T extends IModelElement> T newModelElement( Class<T> type ) throws ModelElementFactoryException;
+
+ public abstract IModelElement newModelElement(String namespaceURI, String localPart) throws ModelElementFactoryException;
+
+ public abstract <T extends IModelElement> void register( Class<T> type, Class<? extends T> impl, String name, String groupName, String groupURI );
+
+ public abstract <T extends IModelElement> void unregister( Class<T> type, Class<? extends T> impl );
+
+ public abstract IModelInfo getModelInfo( Class<? extends IModelElement> type );
+
+ private static ModelElementFactory instance = new DefaultModelElementFactory();
+
+ public static ModelElementFactory getInstance() {
+ return instance;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java
new file mode 100644
index 0000000..c450537
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementFactoryException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+public class ModelElementFactoryException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ModelElementFactoryException(Throwable t) {
+ super(t);
+ }
+
+ public ModelElementFactoryException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java
new file mode 100644
index 0000000..40c64f0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/ModelElementSupport.java
@@ -0,0 +1,728 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.io.Serializable;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Logger;
+
+
+public class ModelElementSupport implements Serializable {
+
+ private static final Logger log = Logger.getLogger( ModelElementSupport.class.getName() );
+
+ private static final long serialVersionUID = 1L;
+
+ private static final PropertyAdapter[] EMPTY_PROPS = new PropertyAdapter[] {};
+ private static final IModelElement[] EMPTY_ELEMENTS = new IModelElement[] {};
+ private static final Object[] ZERO_ARGS = new Object[] {};
+ private static final Class<?>[] ZERO_PARAMS = new Class[] {};
+
+ private static final WeakHashMap<Class<?>, SoftReference<ChildAdapter[]>> adapterCache = new WeakHashMap<Class<?>, SoftReference<ChildAdapter[]>>();;
+ private static final WeakHashMap<Class<?>, SoftReference<PropertyAdapter[]>> propertyCache = new WeakHashMap<Class<?>, SoftReference<PropertyAdapter[]>>();;
+
+ private IModelElement target;
+
+ private transient SoftReference<PropertyAdapter[]> propertyReference;
+ private transient SoftReference<ChildAdapter[]> childrenReference;
+ private transient SoftReference<Set<String>> propertyNameReference;
+
+ public ModelElementSupport(IModelElement target) {
+ this.target = target;
+ }
+
+ public void setProperty(String name, Object value) throws NoSuchMethodException {
+ PropertyAdapter p = findProperty( name, value );
+ if ( p == null ) {
+ throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+ }
+ invoke( target, p.getWriteMethod(), value );
+ }
+
+ public void addProperty(String name, Object value) throws NoSuchMethodException {
+ PropertyAdapter p = findProperty( name, value );
+ if ( p == null ) {
+ throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+ }
+ invoke( target, p.getAddMethod(), value );
+ }
+
+ public void removeProperty(String name, Object value) throws NoSuchMethodException {
+ PropertyAdapter p = findProperty( name, value );
+ if ( p == null ) {
+ throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+ }
+ invoke( target, p.getRemoveMethod(), value );
+ }
+
+ public Object getProperty( String name ) throws NoSuchMethodException {
+ PropertyAdapter p = findProperty( name, null );
+ if ( p == null ) {
+ throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+ }
+ return invoke( target, p.getReadMethod(), ZERO_ARGS );
+ }
+
+ public Set<String> getPropertyNames() {
+ Set<String> names = propertyNameReference == null ? null : propertyNameReference.get();
+
+ if ( names == null ) {
+ names = new HashSet<String>();
+
+ PropertyAdapter[] props = cachedProps( target.getClass() );
+ for ( PropertyAdapter prop : props ) {
+ names.add( prop.getName() );
+ }
+
+ propertyNameReference = new SoftReference<Set<String>>(names);
+ }
+
+ return names;
+ }
+
+ public Object getDefaultPropertyValue(String name) {
+ try {
+ Method m = target.getClass().getMethod( makeDefaultPropertyValue( name ), ZERO_PARAMS );
+ return invoke( target, m, ZERO_ARGS );
+ } catch (SecurityException e) {
+ throw new UndeclaredThrowableException(e);
+ } catch (NoSuchMethodException e) {
+ // fine no default
+ return null;
+ }
+ }
+
+ public Class<?> getPropertyType(String name) throws NoSuchMethodException {
+ PropertyAdapter p = findProperty( name, null );
+ if ( p == null ) {
+ throw new NoSuchMethodException( "No such property " + name + " for type " + target.getClass() );
+ }
+ return p.getPropertyType();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends IModelElement> T[] childrenOfType( Class<T> type ) {
+ ChildAdapter[] adapters = cachedAdapters();
+
+ if ( adapters.length == 0 ) {
+ // return (T[]) EMPTY_ELEMENTS;
+ return ((T[])Array.newInstance(type, 0));
+ }
+
+ ArrayList<T> elements = new ArrayList<T>();
+
+ for ( ChildAdapter adapter : adapters ) {
+ Collection<? extends IModelElement> val = adapter.members(target);
+
+ for ( IModelElement e : val ) {
+ if ( type.isInstance(e) ) {
+ elements.add( (T) e );
+ }
+ }
+ }
+
+ //return elements.toArray( (T[]) EMPTY_ELEMENTS );
+ return elements.toArray((T[])Array.newInstance(type, elements.size()));
+ }
+
+ public IModelElement[] children() {
+ ChildAdapter[] adapters = cachedAdapters();
+
+ if ( adapters.length == 0 ) {
+ return EMPTY_ELEMENTS;
+ }
+
+ ArrayList<IModelElement> elements = new ArrayList<IModelElement>();
+
+ for ( ChildAdapter adapter : adapters ) {
+ elements.addAll( adapter.members(target) );
+ }
+
+ return elements.toArray( EMPTY_ELEMENTS );
+ }
+
+ public boolean addChild(IModelElement element) throws InvalidModelException {
+ if ( element.getParent() == null ) {
+ ChildAdapter[] adapters = cachedAdapters();
+
+ if ( adapters.length > 0 ) {
+ for ( ChildAdapter adapter : adapters ) {
+ if ( adapter.add( target, element ) ) {
+ element.setParent(target);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public boolean removeChild(IModelElement element) {
+ if ( element.getParent() == target ) {
+ ChildAdapter[] adapters = cachedAdapters();
+
+ if ( adapters.length > 0 ) {
+ for ( ChildAdapter adapter : adapters ) {
+ if ( adapter.remove( target, element ) ) {
+ element.setParent( null );
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public Set<Class<? extends IModelElement>> getChildrenTypes(boolean required) {
+ ChildAdapter[] adapters = cachedAdapters();
+
+ if ( adapters.length == 0 ) {
+ return Collections.emptySet();
+ }
+
+ HashSet<Class<? extends IModelElement>> types = new HashSet<Class<? extends IModelElement>>();
+
+ for ( ChildAdapter adapter : adapters ) {
+ if ( adapter.isRequired() == required ) {
+ Class<? extends IModelElement> type = adapter.getType();
+
+ if ( type != null ) {
+ types.add( type );
+ }
+ }
+ }
+
+ return types;
+ }
+
+ private PropertyAdapter findProperty(String name, Object value) {
+ PropertyAdapter[] props = propertyReference == null ? null : propertyReference.get();
+
+ if ( props == null ) {
+ props = cachedProps( target.getClass() );
+ propertyReference = new SoftReference<PropertyAdapter[]>( props );
+ }
+
+ for ( PropertyAdapter prop : props ) {
+ if ( prop.getName().equals( name ) && (value == null || prop.getRawType().isAssignableFrom(value.getClass() ) ) ) {
+ return prop;
+ }
+ }
+
+ return null;
+ }
+
+ private static PropertyAdapter[] cachedProps(
+ Class<? extends IModelElement> type) {
+ SoftReference<PropertyAdapter[]> ref = propertyCache.get( type );
+
+ PropertyAdapter[] props = ref == null ? null : ref.get();
+
+ if ( props == null ) {
+ props = loadProps( type );
+ propertyCache.put( type, new SoftReference<PropertyAdapter[]>( props ) );
+ }
+
+ return props;
+ }
+
+ private static PropertyAdapter[] loadProps(Class<? extends IModelElement> type) {
+ ArrayList<PropertyAdapter> props = new ArrayList<PropertyAdapter>();
+ for ( Method m : type.getMethods() ) {
+ if ( isValidProperty( m )) {
+ try {
+ PropertyAdapter p = new PropertyAdapter( m, type );
+ props.add( p );
+ } catch (NoSuchMethodException e) {
+ // fine not a bean method
+ log.finer( "Invalid bean property method " + m + ": " + e.getMessage() );
+ }
+ }
+ }
+
+ return props.toArray( EMPTY_PROPS );
+ }
+
+ private static boolean isValidProperty(Method m) {
+ return m.getName().startsWith( "get" ) && m.getParameterTypes().length == 0 && !m.getDeclaringClass().equals( Object.class ) && !IModelElement.class.isAssignableFrom(m.getReturnType());
+ }
+
+ private static String makeDefaultPropertyValue(String name) {
+ return "getDefault" + capitalise( name );
+ }
+
+ private static String capitalise(String name) {
+ return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ }
+
+ private static String decapitalise(String substring) {
+ return Character.toLowerCase(substring.charAt(0)) + substring.substring(1);
+ }
+
+ private ChildAdapter[] cachedAdapters() {
+ ChildAdapter[] adapters = childrenReference == null ? null : childrenReference.get();
+
+ if ( adapters == null ) {
+ adapters = loadAdapters( target );
+ childrenReference = new SoftReference<ChildAdapter[]>( adapters );
+ }
+
+ return adapters;
+ }
+
+ private static ChildAdapter[] loadAdapters(IModelElement target) {
+ Class<? extends IModelElement> type = target.getClass();
+ SoftReference<ChildAdapter[]> ref = adapterCache.get( type );
+
+ ChildAdapter[] adapters = ref == null ? null : ref.get();
+
+ if ( adapters == null ) {
+ adapters = buildAdapters( type );
+ adapterCache.put( type, new SoftReference<ChildAdapter[]>( adapters ) );
+ }
+
+ return adapters;
+ }
+
+ private static ChildAdapter[] buildAdapters(Class<? extends IModelElement> type) {
+ ArrayList<ChildAdapter> adapters = new ArrayList<ChildAdapter>();
+
+ for ( Method m : type.getMethods() ) {
+ ChildAdapter adapter = null;
+
+ if ( isValidGetProperty( m ) ) {
+ adapter = buildGetAdapter( m );
+ }
+ else if ( isValidSetProperty( m ) ) {
+ adapter = buildSetAdapter( m );
+ }
+ else if ( isValidAddProperty( m ) ) {
+ adapter = buildAddAdapter( m );
+ }
+ else if ( isValidRemoveProperty( m ) ) {
+ adapter = buildRemoveAdapter( m );
+ }
+
+ if ( adapter != null ) {
+ adapters.add( adapter );
+ }
+ }
+
+ return adapters.toArray( new ChildAdapter[adapters.size()] );
+ }
+
+ private static ChildAdapter buildGetAdapter(Method m) {
+ if ( IModelElement.class.isAssignableFrom( m.getReturnType() ) ) {
+ return new GetPropertyAdapter( m );
+ }
+ else if ( Collection.class.isAssignableFrom( m.getReturnType() ) ) {
+ return new GetCollectionAdapter( m );
+ }
+ else if ( isModelArray( m.getReturnType() ) ) {
+ return new GetArrayAdapter( m );
+ }
+ else {
+ return null;
+ }
+ }
+
+ private static ChildAdapter buildSetAdapter(Method m) {
+ if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+ return new SetPropertyAdapter( m );
+ }
+ else {
+ return null;
+ }
+ }
+
+ private static ChildAdapter buildAddAdapter(Method m) {
+ if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+ return new AddPropertyAdapter( m );
+ }
+ else {
+ return null;
+ }
+ }
+
+ private static ChildAdapter buildRemoveAdapter(Method m) {
+ if ( IModelElement.class.isAssignableFrom( m.getParameterTypes()[0] ) ) {
+ return new RemovePropertyAdapter( m );
+ }
+ else {
+ return null;
+ }
+ }
+
+ private static boolean isValidRemoveProperty(Method m) {
+ return m.getParameterTypes().length == 1 && m.getName().startsWith( "remove" ) && !isDeclared( ICompoundModelElement.class, m );
+ }
+
+ private static boolean isValidAddProperty(Method m) {
+ return m.getParameterTypes().length == 1 && m.getName().startsWith( "add" ) && !isDeclared( ICompoundModelElement.class, m );
+ }
+
+ private static boolean isDeclared(Class<? extends IModelElement> element, Method m) {
+ try {
+ element.getMethod( m.getName(), m.getParameterTypes() );
+ return true;
+ } catch (SecurityException e) {
+ throw new UndeclaredThrowableException( e );
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static boolean isValidSetProperty(Method m) {
+ return m.getParameterTypes().length == 1 && m.getName().startsWith( "set" ) && !isDeclared( IModelElement.class, m );
+ }
+
+ private static boolean isValidGetProperty(Method m) {
+ return m.getParameterTypes().length == 0 && m.getName().startsWith( "get" ) && !isDeclared( IModelElement.class, m ) && !isDeclared(ICompoundModelElement.class, m);
+ }
+
+ private static Object invoke( Object target, Method m, Object... args ) {
+ try {
+ return m.invoke(target, args);
+ } catch (IllegalArgumentException e) {
+ // this should already have been tested
+ throw new IllegalStateException(e);
+ } catch (IllegalAccessException e) {
+ throw new UndeclaredThrowableException( e );
+ } catch (InvocationTargetException e) {
+ throw new UndeclaredThrowableException( e.getCause() );
+ }
+ }
+
+ private static class PropertyAdapter {
+
+ String prop;
+ String name;
+ Method g;
+ Method s;
+ Method a;
+ Method r;
+ Class<?> propertyType;
+
+ public PropertyAdapter(Method g, Class<?> type) throws SecurityException, NoSuchMethodException {
+ if ( g.getReturnType().isArray() || Iterable.class.isAssignableFrom(g.getReturnType() ) ) {
+ prop = g.getName().substring(3);
+ // remove trailing s - as in addWibble, removeWibble, getWibbles
+ prop = prop.substring(0, prop.length() - 1);
+ name = decapitalise( prop );
+ a = find( "add", prop, g.getReturnType(), type );
+ propertyType = a.getParameterTypes()[0];
+ r = find( "remove", prop, g.getReturnType(), type );
+ if ( r.getParameterTypes()[0] != propertyType ) {
+ throw new NoSuchMethodException( "Add remove property method types do not match" );
+ }
+ propertyType = Array.newInstance(propertyType, 0).getClass();
+ }
+ else {
+ prop = g.getName().substring(3);
+ name = decapitalise( prop );
+ propertyType = g.getReturnType();
+ s = find( "set", prop, propertyType, type );
+ }
+
+ this.g = g;
+ }
+
+ public Class<?> getRawType() {
+ return propertyType.isArray() ? propertyType.getComponentType() : propertyType;
+ }
+
+ public Class<?> getPropertyType() {
+ return propertyType;
+ }
+
+ public Method getReadMethod() {
+ return g;
+ }
+
+ public Method getAddMethod() throws NoSuchMethodException {
+ if ( a == null ) {
+ throw new NoSuchMethodException( "No such method add" + prop );
+ }
+
+ return a;
+ }
+
+ public Method getRemoveMethod() throws NoSuchMethodException {
+ if ( r == null ) {
+ throw new NoSuchMethodException( "No such method remove" + prop );
+ }
+
+ return r;
+ }
+
+ public Method getWriteMethod() throws NoSuchMethodException {
+ if ( s == null ) {
+ throw new NoSuchMethodException( "No such method set" + prop );
+ }
+
+ return s;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyAdapter[" + name + "]";
+ }
+
+ private Method find(String prefix, String prop, Class<?> returnType, Class<?> type) throws SecurityException, NoSuchMethodException {
+ String methodName = prefix + prop;
+
+ if ( returnType.isArray() ) {
+ Class<?> t = returnType.getComponentType();
+ return type.getMethod( methodName, new Class[] { t } );
+ }
+ else if ( Iterable.class.isAssignableFrom( returnType ) ) {
+ Method f = null;
+ for ( Method m : type.getMethods() ) {
+ if ( m.getParameterTypes().length == 1 && m.getName().equals( methodName ) && !IModelElement.class.isAssignableFrom(m.getParameterTypes()[0]) ) {
+ if ( f == null ) {
+ f = m;
+ }
+ else {
+ throw new NoSuchMethodException( "Found duplicate " + methodName );
+ }
+ }
+ }
+ if ( f == null ) {
+ throw new NoSuchMethodException( "No such method " + methodName );
+ }
+
+ return f;
+ }
+ else {
+ return type.getMethod( methodName, new Class[] { returnType } );
+ }
+ }
+ public String getName() {
+ return name;
+ }
+
+ }
+
+ private static abstract class ChildAdapter {
+ Method m;
+
+ ChildAdapter( Method m ) {
+ this.m = m;
+ }
+
+ public boolean isRequired() {
+ return m.isAnnotationPresent(Required.class);
+ }
+
+ boolean add(Object target, IModelElement element) {
+ return false;
+ }
+
+ abstract Class<? extends IModelElement> getType();
+
+ boolean remove(Object target, IModelElement element) {
+ return false;
+ }
+
+ Collection<? extends IModelElement> members(Object target) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "ChildAdapter[ " + m.getName() + "]";
+ }
+ }
+
+ private static class GetPropertyAdapter extends ChildAdapter {
+ GetPropertyAdapter(Method m) {
+ super(m);
+ }
+
+ @Override
+ Collection<? extends IModelElement> members(Object target) {
+ IModelElement member = (IModelElement) invoke( target, m, ZERO_ARGS );
+ if ( member == null ) {
+ return Collections.emptyList();
+ }
+ else {
+ return Collections.<IModelElement>singleton( member );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<? extends IModelElement> getType() {
+ return (Class<? extends IModelElement>) m.getReturnType();
+ }
+ }
+
+ private static class GetCollectionAdapter extends ChildAdapter {
+ public GetCollectionAdapter(Method m) {
+ super(m);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Collection<? extends IModelElement> members(Object target) {
+ Collection members = (Collection) invoke( target, m, ZERO_ARGS );
+ if ( members == null ) {
+ return Collections.emptyList();
+ }
+ else {
+ ArrayList<IModelElement> safe = new ArrayList<IModelElement>(members.size());
+ for ( Object o : members ) {
+ if ( o instanceof IModelElement ) {
+ safe.add( (IModelElement) o );
+ }
+ }
+ return safe;
+ }
+ }
+
+ @Override
+ Class<? extends IModelElement> getType() {
+ // impossible to get type of a collection as erasure removes generics info
+ return null;
+ }
+
+ }
+
+ private static class GetArrayAdapter extends ChildAdapter {
+ public GetArrayAdapter(Method m) {
+ super(m);
+ }
+
+ @Override
+ Collection<? extends IModelElement> members(Object target) {
+ IModelElement[] array = (IModelElement[]) invoke( target, m, ZERO_ARGS );
+ if ( array == null || array.length == 0) {
+ return Collections.emptyList();
+ }
+ else {
+ return (Collection<? extends IModelElement>) Arrays.asList( array );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<? extends IModelElement> getType() {
+ return (Class<? extends IModelElement>) m.getReturnType().getComponentType();
+ }
+ }
+
+ private static class SetPropertyAdapter extends ChildAdapter {
+ public SetPropertyAdapter(Method m) {
+ super(m);
+ }
+
+ @Override
+ boolean add(Object target, IModelElement element) {
+ if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+ invoke(target, m, new Object[] { element } );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ boolean remove(Object target, IModelElement element) {
+ if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+ invoke(target, m, new Object[] { null } );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<? extends IModelElement> getType() {
+ return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+ }
+ }
+
+ private static class AddPropertyAdapter extends ChildAdapter {
+ public AddPropertyAdapter(Method m) {
+ super(m);
+ }
+
+ @Override
+ boolean add(Object target, IModelElement element) {
+ if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+ invoke(target, m, new Object[] { element } );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<? extends IModelElement> getType() {
+ return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+ }
+ }
+
+ private static class RemovePropertyAdapter extends ChildAdapter {
+
+ public RemovePropertyAdapter(Method m) {
+ super(m);
+ }
+
+ @Override
+ boolean remove(Object target, IModelElement element) {
+ if ( m.getParameterTypes()[0].isAssignableFrom( element.getClass() ) ) {
+ invoke(target, m, new Object[] { element } );
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<? extends IModelElement> getType() {
+ return (Class<? extends IModelElement>) m.getParameterTypes()[0];
+ }
+ }
+
+ private static boolean isModelArray(Class<?> returnType) {
+ return returnType.isArray() && IModelElement.class.isAssignableFrom(returnType.getComponentType() );
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java
new file mode 100644
index 0000000..c82fea7
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/OverrideOptions.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author dave
+ *
+ */
+public enum OverrideOptions {
+ NO("no"), MAY("may"), MUST("must");
+
+ private String str;
+
+ private static Map<String, OverrideOptions> map = new HashMap<String, OverrideOptions>();
+
+ static {
+ for (OverrideOptions option : OverrideOptions.values()) {
+ map.put(option.str.toLowerCase(), option);
+ }
+ }
+
+ private OverrideOptions(String str) {
+ this.str = str;
+ }
+
+ public static OverrideOptions parse(String val) {
+ OverrideOptions option = map.get(val.toLowerCase());
+
+ if (option == null) {
+ throw new IllegalArgumentException("Invalid override value " + val);
+ }
+
+ return option;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java
new file mode 100644
index 0000000..79b4278
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/Required.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Required {
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java
new file mode 100644
index 0000000..28b83d0
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/And.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class And implements LDAPExpr {
+
+ /**
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+ public static LDAPExpr apply(LDAPExpr... terms) {
+ if (terms == null) {
+ throw new NullPointerException("terms cannot be null");
+ }
+ else if (terms.length == 0) {
+ return Expressions.T;
+ }
+ else if (terms.length == 1) {
+ return terms[0];
+ }
+ LDAPExpr[] filtered = new LDAPExpr[terms.length];
+ int ctr = 0;
+ for (int i = 0; i < terms.length; i++) {
+ if (terms[i].equals(Expressions.F))
+ return Expressions.F;
+ if (terms[i].equals(Expressions.T))
+ continue;
+ filtered[ctr] = terms[i];
+ ctr++;
+ }
+ if (ctr == 0) {
+ return Expressions.T;
+ }
+ else if (ctr == 1) {
+ return filtered[0];
+ }
+ LDAPExpr[] andTerms = new LDAPExpr[ctr];
+ System.arraycopy(filtered, 0, andTerms, 0, ctr);
+
+ return new And(andTerms);
+ }
+
+ private And(LDAPExpr... children) {
+ this.children = children;
+ }
+
+ public boolean eval(Map<String, ?> map) {
+ for (int i = 0; i < children.length; i++) {
+ if (!children[i].eval(map)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void visit(ExprVisitor v) {
+ v.visitAnd(this);
+ }
+
+ public LDAPExpr[] getChildren() {
+ return children;
+ }
+
+ public void setChildren(LDAPExpr[] children) {
+ this.children = children;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof And) {
+ And that = (And) other;
+ if (children.length != that.children.length) {
+ return false;
+ }
+ for (int i = 0; i < children.length; i++) {
+ if (!children[i].equals(that.children[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(256);
+ buf.append("(&");
+ for (int i = 0; i < children.length; i++) {
+ buf.append(" ").append(children[i]).append(" ");
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java
new file mode 100644
index 0000000..ecf105e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Cardinality.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+/**
+ * Immutable class representing cardinality constraints between two entities.
+ *
+ */
+public class Cardinality implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Cardinality ZERO_TO_MANY = new Cardinality(0, -1);
+ public static final Cardinality ONE_TO_MANY = new Cardinality(1, -1);
+ public static final Cardinality ZERO_TO_ONE = new Cardinality(0, 1);
+ public static final Cardinality ONE_TO_ONE = new Cardinality(1, 1);
+
+ private int min;
+ private int max;
+
+ /**
+ * @param min
+ * >=0 (usually 0 or 1)
+ * @param max
+ * >=min or -1 to indicate an unbounded maximum
+ */
+ public Cardinality(int min, int max) {
+ if (min < 0) {
+ throw new IllegalArgumentException("Min cannot be less than 0");
+ }
+
+ if ((max < min) && (max != -1)) {
+ throw new IllegalArgumentException("Max cannot be less than min");
+ }
+
+ this.min = min;
+ this.max = max;
+ }
+
+ public int getMin() {
+ return min;
+ }
+
+ public int getMax() {
+ return max;
+ }
+
+ public String toString() {
+ return min + ".." + ((max == -1) ? ("n") : (Integer.toString(max)));
+ }
+
+ public boolean isDefined(Cardinality cardinality) {
+ return (min <= cardinality.min) && ((max == -1) || (max >= cardinality.max));
+ }
+
+ public boolean isSingleton() {
+ return (min == 1) && (max == 1);
+ }
+
+ public static Cardinality parse(String stringRep) throws IllegalArgumentException {
+ stringRep = stringRep.trim();
+
+ int dotdot = stringRep.indexOf("..");
+
+ if (dotdot == -1) {
+ throw new IllegalArgumentException("Invalid cardinality string representation, expected ..");
+ }
+
+ String minStr = stringRep.substring(0, dotdot);
+ String maxStr = stringRep.substring(dotdot + 2);
+
+ int min = Integer.parseInt(minStr);
+ int max = min;
+
+ if ("n".equals(maxStr)) {
+ max = -1;
+ }
+ else {
+ max = Integer.parseInt(maxStr);
+ }
+
+ return cardinality(min, max);
+ }
+
+ public static Cardinality cardinality(int min, int max) {
+ Cardinality c = null;
+
+ if (min == 0) {
+ if (max == 1) {
+ c = ZERO_TO_ONE;
+ }
+ else if (max == -1) {
+ c = ZERO_TO_MANY;
+ }
+ }
+ else if (min == 1) {
+ if (max == 1) {
+ c = ONE_TO_ONE;
+ }
+ else if (max == -1) {
+ c = ONE_TO_MANY;
+ }
+ }
+
+ if (c == null)
+ c = new Cardinality(min, max);
+
+ return c;
+ }
+
+ public int hashCode() {
+ return max ^ min;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ try {
+ Cardinality c = (Cardinality) o;
+
+ return (min == c.min) && (max == c.max);
+ }
+ catch (ClassCastException cce) {
+ return false;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java
new file mode 100644
index 0000000..e112928
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ExprVisitor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public interface ExprVisitor {
+
+ void visitAnd(And a);
+ void visitOr(Or o);
+ void visitNot(Not n);
+ void visitSimple(SimpleTerm st);
+ // if none of the above matches use this
+ void visitAny(LDAPExpr ex);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java
new file mode 100644
index 0000000..24ea698
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Expressions.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Expressions {
+
+ public static LDAPExpr and(LDAPExpr... terms) {
+ return And.apply(terms);
+ }
+
+ public static LDAPExpr or(LDAPExpr... terms) {
+ return Or.apply(terms);
+ }
+
+ public static LDAPExpr not(LDAPExpr e) {
+ return Not.apply(e);
+ }
+
+ public static LDAPExpr T = Bool.TRUE;
+ public static LDAPExpr F = Bool.FALSE;
+
+ // supports direct use of wildcards for ease of testing, but not literal *s
+ public static SimpleTerm ex(String name, Ops op, String rhs) {
+
+ rhs = rhs.replace('*', SimpleTerm.WILDCARD);
+ return new SimpleTerm(name, op, rhs);
+ }
+
+}
+
+class Bool implements LDAPExpr {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Bool TRUE = new Bool(true);
+ public static final Bool FALSE = new Bool(false);
+
+ private boolean bool;
+
+ public Bool(boolean bool) {
+ this.bool = bool;
+ }
+
+ public boolean eval(Map<String, ?> map) {
+ return bool;
+ }
+
+ public void visit(ExprVisitor v) {
+ }
+
+ public LDAPExpr[] getChildren() {
+ return CHILDLESS;
+ }
+
+ public String toString() {
+ return "(" + bool + ")";
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java
new file mode 100644
index 0000000..8e3ebad
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/FilterValidator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public interface FilterValidator {
+
+ public static FilterValidator ACCEPT_ALL = new AcceptEverythingValidator();
+
+ boolean validate(LDAPExpr filter);
+
+ static class AcceptEverythingValidator implements FilterValidator {
+
+ public boolean validate(LDAPExpr filter) {
+ return true;
+ }
+
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java
new file mode 100644
index 0000000..a009b76
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPExpr.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public interface LDAPExpr extends Serializable {
+
+ public static final LDAPExpr[] CHILDLESS = new LDAPExpr[] {};
+ public static LDAPExpr ACCEPT_ALL = Expressions.T;
+
+ LDAPExpr[] getChildren();
+
+ void visit(ExprVisitor v);
+
+ boolean equals(Object other);
+
+ int hashCode();
+
+ boolean eval(Map<String, ?> map);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java
new file mode 100644
index 0000000..2e67024
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParseException.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public class LDAPParseException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private ParseState ps;
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\\r\\n");
+
+ public LDAPParseException(String message, ParseState ps) {
+ super(message);
+ this.ps = ps;
+ }
+
+ public LDAPParseException(String message) {
+ super(message);
+ }
+
+ @Override
+ public String getMessage() {
+ if (ps == null) {
+ return super.getMessage();
+ }
+
+ String basicMessage = super.getMessage();
+ StringBuffer buf = new StringBuffer(basicMessage.length() + ps.str.length() * 2);
+ buf.append(basicMessage).append(LINE_SEPARATOR);
+ buf.append(ps.str).append(LINE_SEPARATOR);
+ for (int i = 0; i < ps.pos; i++) {
+ buf.append(" ");
+ }
+ buf.append("^");
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java
new file mode 100644
index 0000000..8353041
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/LDAPParser.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import static org.cauldron.sigil.model.common.Expressions.and;
+import static org.cauldron.sigil.model.common.Expressions.not;
+import static org.cauldron.sigil.model.common.Expressions.or;
+import static org.cauldron.sigil.model.common.Ops.APPROX;
+import static org.cauldron.sigil.model.common.Ops.EQ;
+import static org.cauldron.sigil.model.common.Ops.GE;
+import static org.cauldron.sigil.model.common.Ops.GT;
+import static org.cauldron.sigil.model.common.Ops.LE;
+import static org.cauldron.sigil.model.common.Ops.LT;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LDAPParser {
+
+ private static final LDAPParser parser = new LDAPParser();
+
+ public static LDAPExpr parseExpression(String strExpr) throws LDAPParseException {
+ return parser.parse(strExpr);
+ }
+
+ public static void main(String[] args) {
+ for (String arg : args) {
+ try {
+ System.out.println(parseExpression(arg));
+ }
+ catch (LDAPParseException e) {
+ System.out.println("Failed to parse " + arg);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public LDAPExpr parse(String strExpr) throws LDAPParseException {
+
+ if (strExpr == null || strExpr.trim().length() == 0) {
+ return LDAPExpr.ACCEPT_ALL;
+ }
+
+ ParseState ps = new ParseState(strExpr);
+ LDAPExpr expr = parseExpr(ps);
+ ps.skipWhitespace();
+ if (!ps.isEndOfString()) {
+ error("expected end of expression ", ps);
+ }
+ return expr;
+ }
+
+ public LDAPExpr parseExpr(ParseState ps) throws LDAPParseException {
+ ps.skipWhitespace();
+ if (!(ps.peek() == '(')) {
+ error("expected (", ps);
+ }
+ ps.read();
+ LDAPExpr expr = null;
+ ps.skipWhitespace();
+ char ch = ps.peek();
+ switch (ch) {
+ case '&':
+ ps.readAndSkipWhiteSpace();
+ List<LDAPExpr> andList = new ArrayList<LDAPExpr>();
+ while (ps.peek() == '(') {
+ andList.add(parseExpr(ps));
+ ps.skipWhitespace();
+ }
+ LDAPExpr[] andArr = andList.toArray(new LDAPExpr[andList.size()]);
+ expr = and(andArr);
+ break;
+ case '|':
+ ps.readAndSkipWhiteSpace();
+ List<LDAPExpr> orList = new ArrayList<LDAPExpr>();
+ while (ps.peek() == '(') {
+ orList.add(parseExpr(ps));
+ ps.skipWhitespace();
+ }
+ LDAPExpr[] orArray = orList.toArray(new LDAPExpr[orList.size()]);
+ expr = or(orArray);
+ break;
+ case '!':
+ ps.readAndSkipWhiteSpace();
+ expr = not(parseExpr(ps));
+ break;
+ default:
+ if (isNameChar(ch)) {
+ expr = parseSimple(ps);
+ }
+ else {
+ error("unexpected character: '" + ch + "'", ps);
+ }
+ }
+ ps.skipWhitespace();
+ if (ps.peek() != ')') {
+ error("expected )", ps);
+ }
+ ps.read();
+ return expr;
+
+ }
+
+ void error(String message, ParseState ps) throws LDAPParseException {
+ throw new LDAPParseException(message, ps);
+ }
+
+ private SimpleTerm parseSimple(ParseState ps) throws LDAPParseException {
+ // read name
+ StringBuffer name = new StringBuffer(16);
+ for (char c = ps.peek(); !ps.isEndOfString() && isNameChar(c); c = ps.peek()) {
+ ps.read();
+ name.append(c);
+ }
+ ps.skipWhitespace();
+ Ops op = null;
+ // read op
+ if (ps.lookingAt("=")) {
+ op = EQ;
+ ps.skip(1);
+ }
+ else if (ps.lookingAt(">=")) {
+ op = GE;
+ ps.skip(2);
+ }
+ else if (ps.lookingAt("<=")) {
+ op = LE;
+ ps.skip(2);
+ }
+ else if (ps.lookingAt(">")) {
+ op = GT;
+ ps.skip(1);
+ }
+ else if (ps.lookingAt("<")) {
+ op = LT;
+ ps.skip(1);
+ }
+ else if (ps.lookingAt("-=")) {
+ op = APPROX;
+ ps.skip(2);
+ }
+ else if (ps.isEndOfString()) {
+ error("unexpected end of expression", ps);
+ }
+ else {
+ error("unexpected character: '" + ps.peek() + "'", ps);
+ }
+ ps.skipWhitespace();
+
+ boolean escaped = false;
+ StringBuffer value = new StringBuffer(16);
+
+ while (!ps.isEndOfString() && !Character.isWhitespace(ps.peek()) && !(ps.peek() == ')' && !escaped)) {
+
+ char ch = ps.peek();
+
+ if (ch == '\\') {
+ escaped = true;
+ ps.read();
+ }
+ else if (ch == '*') {
+ if (escaped) {
+ value.append(ch);
+ escaped = false;
+ }
+ else {
+ value.append(SimpleTerm.WILDCARD);
+ }
+ ps.read();
+ }
+ else if (isLiteralValue(ch)) {
+ if (escaped) {
+ error("incorrectly applied escape of '" + ch + "'", ps);
+ }
+ value.append(ps.read());
+ }
+ else if (isEscapedValue(ch)) {
+ if (!escaped) {
+ error("missing escape for '" + ch + "'", ps);
+ }
+ value.append(ps.read());
+ escaped = false;
+ }
+ else {
+ error("unexpected character: '" + ps.peek() + "'", ps);
+ }
+ }
+ ps.skipWhitespace();
+
+ SimpleTerm expr = new SimpleTerm(name.toString(), op, value.toString());
+
+ return expr;
+ }
+
+ private boolean isNameChar(int ch) {
+ return !(Character.isWhitespace(ch) || (ch == '(') || (ch == ')') || (ch == '<') || (ch == '>') || (ch == '=')
+ || (ch == '~') || (ch == '*') || (ch == '\\'));
+ }
+
+ private boolean isLiteralValue(int ch) {
+ return !(Character.isWhitespace(ch) || (ch == '(') || (ch == ')') || (ch == '*'));
+ }
+
+ private boolean isEscapedValue(int ch) {
+ return (ch == '(') || (ch == ')') || (ch == '*');
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java
new file mode 100644
index 0000000..a3b59c3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Not.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Not implements LDAPExpr {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+ public static LDAPExpr apply(LDAPExpr e) {
+ if (e == null) {
+ throw new NullPointerException("cannot apply Not to a null expression");
+ }
+ if (e.equals(Expressions.T)) {
+ return Expressions.F;
+ }
+ if (e.equals(Expressions.F)) {
+ return Expressions.T;
+ }
+ return new Not(e);
+ }
+
+ private Not(LDAPExpr child) {
+ this.children = new LDAPExpr[] { child };
+ }
+
+ public boolean eval(Map<String, ?> map) {
+ return !children[0].eval(map);
+ }
+
+ public LDAPExpr getEx() {
+ return children[0];
+ }
+
+ public void visit(ExprVisitor v) {
+ v.visitNot(this);
+ }
+
+ public LDAPExpr[] getChildren() {
+ return children;
+ }
+
+ public void setChild(LDAPExpr child) {
+ this.children = new LDAPExpr[] { child };
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Not) {
+ Not that = (Not) other;
+ return children[0].equals(that.children[0]);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(256);
+ buf.append("(!");
+ buf.append(" ").append(children[0]).append(" ");
+ buf.append(")");
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java
new file mode 100644
index 0000000..570fe8d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Ops.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public enum Ops {
+ EQ, GE, LE, GT, LT, APPROX;
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case EQ:
+ return "=";
+ case GE:
+ return ">=";
+ case LE:
+ return "<=";
+ case GT:
+ return ">";
+ case LT:
+ return "<";
+ case APPROX:
+ return "~=";
+ default:
+ return super.toString();
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java
new file mode 100644
index 0000000..39f3470
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Or.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.Map;
+
+public class Or implements LDAPExpr {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private LDAPExpr[] children;
+
+ public static LDAPExpr apply(LDAPExpr... terms) {
+ if (terms == null) {
+ throw new NullPointerException("terms cannot be null");
+ }
+ else if (terms.length == 0) {
+ return Expressions.T;
+ }
+ else if (terms.length == 1) {
+ return terms[0];
+ }
+ LDAPExpr[] filtered = new LDAPExpr[terms.length];
+ int ctr = 0;
+ for (int i = 0; i < terms.length; i++) {
+ if (terms[i].equals(Expressions.T))
+ return Expressions.T;
+ if (terms[i].equals(Expressions.F))
+ continue;
+ filtered[ctr] = terms[i];
+ ctr++;
+ }
+ if (ctr == 0) {
+ return Expressions.F;
+ }
+ else if (ctr == 1) {
+ return filtered[0];
+ }
+ LDAPExpr[] orTerms = new LDAPExpr[ctr];
+ System.arraycopy(filtered, 0, orTerms, 0, ctr);
+
+ return new Or(orTerms);
+ }
+
+ private Or(LDAPExpr... children) {
+ this.children = children;
+ }
+
+ public boolean eval(Map<String, ?> map) {
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].eval(map)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void visit(ExprVisitor v) {
+ v.visitOr(this);
+ }
+
+ public LDAPExpr[] getChildren() {
+ return children;
+ }
+
+ public void setChildren(LDAPExpr[] children) {
+ this.children = children;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Or) {
+ Or that = (Or) other;
+ if (children.length != that.children.length) {
+ return false;
+ }
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].equals(that.children[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(256);
+ buf.append("(|");
+ for (int i = 0; i < children.length; i++) {
+ buf.append(" ").append(children[i]).append(" ");
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java
new file mode 100644
index 0000000..a6f23ac
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/ParseState.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+/**
+ * @author dave
+ *
+ */
+class ParseState implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ int pos;
+
+ String str;
+
+ ParseState(String str) {
+ this.str = str;
+ }
+
+ public boolean lookingAt(String start) {
+ return str.substring(pos).startsWith(start);
+ }
+
+ public CharSequence skip(int n) {
+ int end = pos + n < str.length() ? pos + n : str.length();
+ int start = pos;
+ pos = end;
+ return str.subSequence(start, end);
+ }
+
+ public char read() {
+ char ch = str.charAt(pos);
+ if (pos < str.length()) {
+ pos++;
+ }
+ return ch;
+ }
+
+ public char readAndSkipWhiteSpace() {
+ char ch = read();
+ skipWhitespace();
+ return ch;
+ }
+
+ char peek() {
+ if (isEndOfString()) {
+ return (char) -1;
+ }
+ return str.charAt(pos);
+ }
+
+ boolean isEndOfString() {
+ return pos == str.length();
+ }
+
+ void skipWhitespace() {
+ while (pos < str.length() && Character.isWhitespace(str.charAt(pos))) {
+ pos++;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java
new file mode 100644
index 0000000..ebc4517
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/SimpleTerm.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.lang.reflect.Constructor;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+public class SimpleTerm implements LDAPExpr {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final char WILDCARD = 2 ^ 16 - 1;
+ private static final String WILDCARD_STRING = new String(new char[] { SimpleTerm.WILDCARD });
+
+ private Ops op;
+ private String name;
+ private String rval;
+
+ public SimpleTerm(String name, Ops op, String value) {
+ this.op = op;
+ this.name = name.intern();
+ this.rval = value.intern();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Ops getOp() {
+ return op;
+ }
+
+ public String getRval() {
+ return rval;
+ }
+
+ public boolean eval(Map<String, ?> map) {
+
+ Object lval = map.get(name);
+ if (lval == null) {
+ return false;
+ }
+ else if (Ops.EQ == op && WILDCARD_STRING.equals(lval)) {
+ return true;
+ }
+ // any match in the vector will do
+ else if (lval instanceof Vector<?>) {
+ Vector<?> vec = (Vector<?>) lval;
+ for (Iterator<?> i = vec.iterator(); i.hasNext();) {
+ if (check(i.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // any match in the array will do
+ else if (lval instanceof Object[]) {
+ Object[] arr = (Object[]) lval;
+ for (int i = 0; i < arr.length; i++) {
+ if (check(arr[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return check(lval);
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean check(Object lval) {
+ if (lval == null) {
+ return false;
+ }
+ else if (Ops.EQ == op && WILDCARD_STRING.equals(lval)) {
+ return true;
+ }
+
+ Object rhs = null;
+
+ if (lval instanceof String) {
+
+ if (Ops.APPROX == op) {
+ rhs = collapseWhiteSpace(rval);
+ lval = collapseWhiteSpace((String) lval);
+ }
+
+ if (Ops.EQ == op || Ops.APPROX == op) {
+ return stringCheck((String) lval);
+ }
+ // rhs already a string
+
+ }
+ else if (lval.getClass() == Byte.class) {
+ rhs = Byte.valueOf(rval);
+ }
+ else if (lval.getClass() == Short.class) {
+ rhs = Short.valueOf(rval);
+ }
+ else if (lval.getClass() == Integer.class) {
+ rhs = Integer.valueOf(rval);
+ }
+ else if (lval.getClass() == Long.class) {
+ rhs = Long.valueOf(rval);
+ }
+ else if (lval.getClass() == Float.class) {
+ rhs = Float.valueOf(rval);
+ }
+ else if (lval.getClass() == Double.class) {
+ rhs = Double.valueOf(rval);
+ }
+ else {
+ try {
+ Constructor<?> stringCtor = lval.getClass().getConstructor(new Class[] { String.class });
+ rhs = stringCtor.newInstance(rval);
+ }
+ catch (Exception e) {
+ // log it
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ if (!(lval instanceof Comparable)) {
+ return Ops.EQ == op && lval.equals(rval);
+ }
+ else {
+
+ Comparable<? super Object> lhs = (Comparable<? super Object>) lval;
+
+ int compare = lhs.compareTo(rhs);
+
+ switch (op) {
+ case EQ:
+ return compare == 0;
+ case APPROX:
+ return compare == 0;
+ case GE:
+ return compare >= 0;
+ case LE:
+ return compare <= 0;
+ case GT:
+ return compare > 0;
+ case LT:
+ return compare < 0;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean stringCheck(String lhs) {
+
+ String rhs;
+ switch (op) {
+ case EQ:
+ case APPROX:
+ rhs = rval;
+ break;
+ default:
+ return false;
+ }
+
+ int valLength = lhs.length();
+ int patLength = rval.length();
+
+ if (valLength == 0 && patLength == 0) {
+ return true;
+ }
+
+ boolean wc = false;
+ int j = 0;
+ for (int i = 0; i < patLength; i++) {
+ // trailing wildcards
+ char pc = rhs.charAt(i);
+ if (j == valLength) {
+ if (pc != SimpleTerm.WILDCARD) {
+ return false;
+ }
+ continue;
+ }
+ if (pc == SimpleTerm.WILDCARD) {
+ wc = true;
+ continue;
+ }
+ while (wc && j < valLength - 1 && lhs.charAt(j) != pc) {
+ j++;
+ }
+ if (lhs.charAt(j) != pc) {
+ return false;
+ }
+ else {
+ wc = false;
+ j++;
+ }
+ }
+ return (wc || j == valLength);
+
+ }
+
+ private String collapseWhiteSpace(String in) {
+ StringBuffer out = new StringBuffer(in.trim().length());
+ boolean white = false;
+ for (int i = 0; i < in.length(); i++) {
+ char ch = in.charAt(i);
+ if (Character.isWhitespace(ch)) {
+ white = true;
+ }
+ else {
+ if (white) {
+ out.append(" ");
+ white = false;
+ }
+ out.append(ch);
+ }
+ }
+ return out.toString();
+ }
+
+ public void visit(ExprVisitor v) {
+ v.visitSimple(this);
+ }
+
+ public LDAPExpr[] getChildren() {
+ return CHILDLESS;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof SimpleTerm) {
+ SimpleTerm that = (SimpleTerm) other;
+ return name.equals(that.name) && op.equals(that.op) && rval.equals(that.rval);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + name + " " + op.toString() + " " + escape(rval) + ")";
+ }
+
+ private String escape(String raw) {
+ StringBuffer buf = new StringBuffer(raw.length() + 10);
+ for (int i = 0; i < raw.length(); i++) {
+ char ch = raw.charAt(i);
+ switch (ch) {
+ case SimpleTerm.WILDCARD:
+ buf.append("*");
+ break;
+ case '(':
+ case ')':
+ case '*':
+ buf.append("\\").append(ch);
+ break;
+ default:
+ buf.append(ch);
+ }
+ }
+ return buf.toString();
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java
new file mode 100644
index 0000000..0b6b30b
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/Utils.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Utils {
+ public static MapBuilder map(String name, Object value) {
+ return new MapBuilder().put(name, value);
+ }
+
+ public static String toString(Map<String, Object> attrs) {
+ if (attrs == null) {
+ return "NULL";
+ }
+
+ StringBuffer buf = new StringBuffer(128);
+ List<String> keys = new ArrayList<String>(attrs.keySet());
+ Collections.sort(keys);
+ buf.append("{");
+
+ for (int i = 0; i < keys.size(); i++) {
+ Object name = keys.get(i);
+ Object value = attrs.get(name);
+ buf.append(name).append("=").append(value).append(",");
+ }
+
+ if (buf.length() > 1) {
+ buf.delete(buf.length() - 1, buf.length());
+ }
+
+ buf.append("}");
+
+ return buf.toString();
+ }
+
+ public static class MapBuilder {
+ private Map<String, Object> map = new HashMap<String, Object>();
+
+ public MapBuilder put(String name, Object value) {
+ map.put(name, value);
+
+ return this;
+ }
+
+ public Map<String, Object> toMap() {
+ return map;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java
new file mode 100644
index 0000000..3505df4
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRange.java
@@ -0,0 +1,252 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+import java.io.Serializable;
+
+import org.osgi.framework.Version;
+
+public class VersionRange implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final Version INFINITE_VERSION = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE,
+ Integer.MAX_VALUE, "");
+ public static final VersionRange ANY_VERSION = new VersionRange(false, Version.emptyVersion, INFINITE_VERSION, true);
+
+ private boolean openFloor;
+ private Version floor;
+ private Version ceiling;
+ private boolean openCeiling;
+
+ /**
+ * Interval constructor
+ *
+ * @param openFloor Whether the lower bound of the range is inclusive (false) or exclusive (true).
+ * @param floor The lower bound version of the range.
+ * @param ceiling The upper bound version of the range.
+ * @param openCeiling Whether the upper bound of the range is inclusive (false) or exclusive (true).
+ */
+ public VersionRange(boolean openFloor, Version floor, Version ceiling, boolean openCeiling) {
+ this.openFloor = openFloor;
+ this.floor = floor;
+ this.ceiling = ceiling;
+ this.openCeiling = openCeiling;
+ }
+
+ /**
+ * atLeast constructor
+ *
+ * @param openFloor
+ * @param floor
+ */
+ public VersionRange(Version atLeast) {
+ this.openFloor = false;
+ this.floor = atLeast;
+ this.ceiling = INFINITE_VERSION;
+ this.openCeiling = true;
+ }
+
+ public static VersionRange parseVersionRange(String val) throws IllegalArgumentException, NumberFormatException {
+ if ( val == null || val.trim().length() == 0 ) {
+ return ANY_VERSION;
+ }
+
+ boolean openFloor;
+ boolean openCeiling;
+ val = val.replaceAll("\\s", "");
+ val = val.replaceAll("\"", "");
+ int fst = val.charAt(0);
+ if (fst == '[') {
+ openFloor = false;
+ }
+ else if (fst == '(') {
+ openFloor = true;
+ }
+ else {
+ Version atLeast = Version.parseVersion(val);
+ return new VersionRange(atLeast);
+ }
+
+ int lst = val.charAt(val.length() - 1);
+ if (lst == ']') {
+ openCeiling = false;
+ }
+ else if (lst == ')') {
+ openCeiling = true;
+ }
+ else {
+ throw new IllegalArgumentException("illegal version range syntax " + val + ": range must end in ')' or ']'");
+ }
+
+ String inner = val.substring(1, val.length() - 1);
+ String[] floorCeiling = inner.split(",");
+ if (floorCeiling.length != 2) {
+ throw new IllegalArgumentException("illegal version range syntax " + "too many commas");
+ }
+ Version floor = Version.parseVersion(floorCeiling[0]);
+ Version ceiling = "*".equals( floorCeiling[1] ) ? INFINITE_VERSION : Version.parseVersion(floorCeiling[1]);
+ return new VersionRange(openFloor, floor, ceiling, openCeiling);
+ }
+ public Version getCeiling() {
+ return ceiling;
+ }
+
+ public Version getFloor() {
+ return floor;
+ }
+
+ public boolean isOpenCeiling() {
+ return openCeiling;
+ }
+
+ public boolean isOpenFloor() {
+ return openFloor;
+ }
+
+ public boolean isPointVersion() {
+ return !openFloor && !openCeiling && floor.equals(ceiling);
+ }
+
+ /**
+ * test a version to see if it falls in the range
+ *
+ * @param version
+ * @return
+ */
+ public boolean contains(Version version) {
+ if (version.equals(INFINITE_VERSION)) {
+ return ceiling.equals(INFINITE_VERSION);
+ }
+ else {
+ return (version.compareTo(floor) > 0 && version.compareTo(ceiling) < 0)
+ || (!openFloor && version.equals(floor)) || (!openCeiling && version.equals(ceiling));
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((ceiling == null) ? 0 : ceiling.hashCode());
+ result = prime * result + ((floor == null) ? 0 : floor.hashCode());
+ result = prime * result + (openCeiling ? 1231 : 1237);
+ result = prime * result + (openFloor ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final VersionRange other = (VersionRange) obj;
+ if (ceiling == null) {
+ if (other.ceiling != null)
+ return false;
+ }
+ else if (!ceiling.equals(other.ceiling))
+ return false;
+ if (floor == null) {
+ if (other.floor != null)
+ return false;
+ }
+ else if (!floor.equals(other.floor))
+ return false;
+ if (openCeiling != other.openCeiling)
+ return false;
+ if (openFloor != other.openFloor)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (ANY_VERSION.equals(this)) {
+ return makeString(openFloor, Version.emptyVersion, INFINITE_VERSION, openCeiling);
+ }
+ return makeString(openFloor, floor, ceiling, openCeiling);
+ }
+
+ private String makeString(boolean openFloor, Version floor, Version ceiling, boolean openCeiling) {
+ StringBuffer vr = new StringBuffer(32);
+ if ( INFINITE_VERSION.equals(ceiling) ) {
+ vr.append( Version.emptyVersion.equals(floor) ? "0" : floor.toString() );
+ }
+ else {
+ vr.append(openFloor ? "(" : "[");
+ String floorStr = Version.emptyVersion.equals(floor) ? "0" : floor.toString();
+ String ceilingStr = ceiling.toString();
+ vr.append(floorStr).append(",").append(ceilingStr);
+ vr.append(openCeiling ? ")" : "]");
+ }
+ return vr.toString();
+ }
+
+
+ public static VersionRange newInstance(Version pointVersion, VersionRangeBoundingRule lowerBoundRule, VersionRangeBoundingRule upperBoundRule) {
+ Version floor = null;
+ switch (lowerBoundRule) {
+ case Any:
+ floor = new Version(0, 0, 0);
+ break;
+ case Major:
+ floor = new Version(pointVersion.getMajor(), 0, 0);
+ break;
+ case Minor:
+ floor = new Version(pointVersion.getMajor(), pointVersion.getMinor(), 0);
+ break;
+ case Micro:
+ floor = new Version(pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro());
+ break;
+ case Exact:
+ floor = pointVersion;
+ break;
+ }
+
+ Version ceiling = null;
+ boolean openCeiling = true;
+ switch (upperBoundRule) {
+ case Any:
+ ceiling = INFINITE_VERSION;
+ break;
+ case Major:
+ ceiling = new Version(pointVersion.getMajor() + 1, 0, 0);
+ break;
+ case Minor:
+ ceiling = new Version(pointVersion.getMajor(), pointVersion.getMinor() + 1, 0);
+ break;
+ case Micro:
+ ceiling = new Version(pointVersion.getMajor(), pointVersion.getMinor(), pointVersion.getMicro() + 1);
+ break;
+ case Exact:
+ ceiling = pointVersion;
+ openCeiling = false;
+ break;
+ }
+
+ return new VersionRange(false, floor, ceiling, openCeiling);
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java
new file mode 100644
index 0000000..a92139d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/common/VersionRangeBoundingRule.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.common;
+
+public enum VersionRangeBoundingRule {
+ Exact, Micro, Minor, Major, Any
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java
new file mode 100644
index 0000000..aa2b25a
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/IDownloadJar.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+public interface IDownloadJar extends IModelElement {
+ void addEntry(IPath entry);
+ void removeEntry(IPath entry);
+ // XXX bad spelling on purpose so that ModelElementSupport picks up method
+ // TODO fix in ModelElementSupport
+ Set<IPath> getEntrys();
+
+ void clearEntries();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java
new file mode 100644
index 0000000..b89dd1c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibrary.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.osgi.framework.Version;
+
+public interface ILibrary extends IModelElement {
+ String getName();
+ void setName(String name);
+ Version getVersion();
+ void setVersion(Version version);
+ void addImport(IPackageImport pi);
+ void removeImport(IPackageImport pi);
+ Collection<IPackageImport> getImports();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java
new file mode 100644
index 0000000..8d53c39
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ILibraryImport.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface ILibraryImport extends IModelElement {
+ String getLibraryName();
+ void setLibraryName(String name);
+ VersionRange getVersions();
+ void setVersions(VersionRange range);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java
new file mode 100644
index 0000000..38e14c6
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/INewtonSystem.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * @author dave
+ *
+ */
+public interface INewtonSystem extends IModelElement {
+ IPath getLocation();
+
+ void setLocation(IPath location);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java
new file mode 100644
index 0000000..6205744
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISCAComposite.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * @author dave
+ *
+ */
+public interface ISCAComposite extends IModelElement {
+ IPath getLocation();
+
+ void setLocation(IPath location);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java
new file mode 100644
index 0000000..54a80dd
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/eclipse/ISigilBundle.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.eclipse;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IVersionedModelElement;
+
+/**
+ * @author dave
+ *
+ */
+public interface ISigilBundle extends ICompoundModelElement, IVersionedModelElement {
+ void synchronize(IProgressMonitor monitor) throws IOException;
+
+ boolean isSynchronized();
+
+ IBundleModelElement getBundleInfo();
+
+ String getSymbolicName();
+
+ void setBundleInfo(IBundleModelElement bundle);
+
+ IDownloadJar getDownloadJar();
+
+ void setDownloadJar(IDownloadJar download);
+
+ void addComposite(ISCAComposite composite);
+
+ void removeComposite(ISCAComposite composite);
+
+ Set<ISCAComposite> getComposites();
+
+ void addLibraryPath(IPath path);
+
+ void removeLibraryPath(IPath path);
+
+ Set<IPath> getLibraryPaths();
+
+ void addSourcePath(IPath path);
+
+ void removeSourcePath(IPath path);
+
+ Set<IPath> getSourcePaths();
+
+ void clearSourcePaths();
+
+ Set<String> getClasspathEntrys();
+
+ void addClasspathEntry(String encodedClasspath);
+
+ void removeClasspathEntry(String encodedClasspath);
+
+ IPath getLocation();
+
+ void setLocation(IPath location);
+
+ IPath getSourcePathLocation();
+
+ void setSourcePathLocation( IPath location );
+
+ IPath getSourceRootPath();
+
+ void setSourceRootPath( IPath location );
+
+ void setLicencePathLocation(IPath cacheSourceLocation);
+
+ IPath getLicencePathLocation();
+
+ /**
+ * get package names included in bundle.
+ * Can contain wildcards e.g. org.foo.*
+ */
+ Set<String> getPackages();
+
+ /**
+ * remove package name from those included in bundle.
+ */
+ boolean removePackage(String pkg);
+
+ /**
+ * add package name to be included in bundle.
+ */
+ void addPackage(String pkg);
+
+
+ /**
+ * get package names included in download jar.
+ * Can contain wildcards e.g. org.foo.*
+ */
+ Set<String> getDownloadPackages();
+
+ /**
+ * remove package name from those included in download jar.
+ */
+ boolean removeDownloadPackage(String pkg);
+
+ /**
+ * add package name to be included in download jar.
+ */
+ void addDownloadPackage(String pkg);
+
+ /**
+ * Attempt to find a package export that matches the given name or return null if none specified
+ *
+ * @param elementName
+ * @return
+ */
+ IPackageExport findExport(String elementName);
+
+ /**
+ * Attempt to find a package import that matches the given name or return null if none specified
+ * @param packageName
+ * @return
+ */
+ IPackageImport findImport(String packageName);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java
new file mode 100644
index 0000000..1f2b8ad
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IBundleModelElement.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Set;
+
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.INamedModelElement;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.osgi.framework.Version;
+
+public interface IBundleModelElement extends INamedModelElement, ICompoundModelElement, IVersionedModelElement {
+
+ String getActivator();
+
+ void setActivator(String activator);
+
+ String getCategory();
+
+ void setCategory(String category);
+
+ String getContactAddress();
+
+ void setContactAddress(String contactAddress);
+
+ String getCopyright();
+
+ void setCopyright(String copyright);
+
+ URI getDocURI();
+
+ void setDocURI(URI docURI);
+
+ Set<IPackageExport> getExports();
+
+ void addExport(IPackageExport packageExport);
+
+ void removeExport(IPackageExport packageExport);
+
+ Set<IPackageImport> getImports();
+
+ void addImport(IPackageImport packageImport);
+
+ void removeImport(IPackageImport packageImport);
+
+ Set<IRequiredBundle> getRequiredBundles();
+
+ void addRequiredBundle(IRequiredBundle bundle);
+
+ void removeRequiredBundle(IRequiredBundle bundle);
+
+ void addLibraryImport(ILibraryImport library);
+
+ void removeLibraryImport(ILibraryImport library);
+
+ Set<ILibraryImport> getLibraryImports();
+
+ URI getLicenseURI();
+
+ void setLicenseURI(URI licenseURI);
+
+ URI getSourceLocation();
+
+ void setSourceLocation(URI sourceLocation);
+
+ String getSymbolicName();
+
+ void setSymbolicName(String symbolicName);
+
+ URI getUpdateLocation();
+
+ void setUpdateLocation(URI updateLocation);
+
+ String getVendor();
+
+ void setVendor(String vendor);
+
+ Version getVersion();
+
+ void setVersion(Version version);
+
+ void setDescription(String elementText);
+
+ String getDescription();
+
+ Collection<String> getClasspaths();
+
+ void addClasspath(String path);
+
+ void removeClasspath(String path);
+
+ void setFragmentHost(IRequiredBundle fragmentHost);
+
+ IRequiredBundle getFragmentHost();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java
new file mode 100644
index 0000000..008b56e
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageExport.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import java.util.Collection;
+
+import org.osgi.framework.Version;
+
+public interface IPackageExport extends IPackageModelElement, IVersionedModelElement, Comparable<IPackageExport> {
+ void addUse(String uses);
+
+ void removeUse(String uses);
+
+ Collection<String> getUses();
+
+ void setUses(Collection<String> asList);
+
+ Version getRawVersion();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java
new file mode 100644
index 0000000..c9a7704
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageImport.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IRequirementModelElement;
+
+public interface IPackageImport extends IPackageModelElement, IVersionRangeModelElement, IRequirementModelElement, Comparable<IPackageImport> {
+ /**
+ * indicates whether the OSGi attribute "resolution=optional" is specified.
+ */
+ boolean isOptional();
+
+ void setOptional(boolean optional);
+
+ /**
+ * indicates whether import is needed at compile-time.
+ * Default true. Used in conjunction with OSGiHeader.ALWAYS,
+ * to add an OSGI import, without creating a dependency.
+ */
+ boolean isDependency();
+
+ void setDependency(boolean dependency);
+
+ /**
+ * indicates whether import should be added to OSGi Package-Import header.
+ * Default: AUTO.
+ */
+ OSGiImport getOSGiImport();
+
+ void setOSGiImport(OSGiImport osgiImport);
+
+ enum OSGiImport {
+ /**
+ * only add to OSGi header, if it appears to be needed.
+ */
+ AUTO,
+
+ /**
+ * always add to OSGi header, even if it appears unnecessary.
+ */
+ ALWAYS,
+
+ /**
+ * never add to OSGi header.
+ */
+ NEVER
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java
new file mode 100644
index 0000000..3e40d45
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IPackageModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public interface IPackageModelElement extends IModelElement {
+
+ String getPackageName();
+
+ void setPackageName(String packageName);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java
new file mode 100644
index 0000000..9f75a43
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IRequiredBundle.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IRequirementModelElement;
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface IRequiredBundle extends IModelElement, IRequirementModelElement, Comparable<IRequiredBundle> {
+ String getSymbolicName();
+
+ void setSymbolicName(String symbolicName);
+
+ VersionRange getVersions();
+
+ void setVersions(VersionRange versions);
+
+ boolean isOptional();
+
+ void setOptional(boolean optional);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java
new file mode 100644
index 0000000..dfc7382
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionRangeModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface IVersionRangeModelElement {
+
+ VersionRange getVersions();
+
+ void setVersions(VersionRange version);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java
new file mode 100644
index 0000000..10c60c9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/model/osgi/IVersionedModelElement.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.osgi;
+
+import org.osgi.framework.Version;
+
+public interface IVersionedModelElement {
+
+ Version getVersion();
+
+ void setVersion(Version version);
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java
new file mode 100644
index 0000000..850de43
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractBundleRepository.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.bld.core.licence.ILicenseManager;
+import org.cauldron.bld.core.licence.ILicensePolicy;
+import org.cauldron.bld.core.util.QuoteUtil;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.osgi.framework.Version;
+
+public abstract class AbstractBundleRepository implements IBundleRepository {
+
+ private final String id;
+ private final HashSet<IBundleRepositoryListener> listeners = new HashSet<IBundleRepositoryListener>();
+
+ public AbstractBundleRepository(String id) {
+ this.id = id;
+ }
+
+ public abstract void accept(IRepositoryVisitor visitor, int options);
+
+ public void addBundleRepositoryListener(IBundleRepositoryListener listener) {
+ synchronized(listeners) {
+ listeners.add(listener);
+ }
+ }
+
+ public void removeBundleRepositoryListener(IBundleRepositoryListener listener) {
+ synchronized(listeners) {
+ listeners.remove(listener);
+ }
+ }
+
+ protected void notifyChange() {
+ for ( IBundleRepositoryListener l : listeners ) {
+ l.notifyChange(this);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void accept(IRepositoryVisitor visitor) {
+ accept( visitor, 0 );
+ }
+
+ public void writeOBR(OutputStream out) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Collection<ISigilBundle> findProviders(final ILibrary library, int options) {
+ final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ final ILicensePolicy policy = findPolicy(library);
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ if (policy.accept(bundle)) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ for ( IPackageImport pi : library.getImports() ) {
+ for ( IPackageExport e : info.getExports() ) {
+ if ( pi.getPackageName().equals( e.getPackageName() ) && pi.getVersions().contains( e.getVersion() ) ) {
+ found.add(bundle);
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ };
+
+ accept( visitor, options );
+
+ return found;
+ }
+
+ public Collection<ISigilBundle> findAllProviders(final IRequiredBundle req, int options) {
+ final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ final ILicensePolicy policy = findPolicy(req);
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ if (policy.accept(bundle)) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ if ( req.getSymbolicName().equals( info.getSymbolicName() ) && req.getVersions().contains( info.getVersion() ) ) {
+ found.add(bundle);
+ }
+ }
+ return true;
+ }
+ };
+
+ accept( visitor, options );
+
+ return found;
+ }
+
+ public Collection<ISigilBundle> findAllProviders(final IPackageImport pi, int options) {
+ final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ final ILicensePolicy policy = findPolicy(pi);
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+
+ public boolean visit(ISigilBundle bundle) {
+ if (policy.accept(bundle)) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ if ( info != null ) {
+ for ( IPackageExport e : info.getExports() ) {
+ if ( pi.getPackageName().equals( e.getPackageName() ) ) {
+ if ( pi.getVersions().contains( e.getVersion() ) ) {
+ found.add(bundle);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ };
+
+ accept( visitor, options );
+
+ return found;
+ }
+
+ public ISigilBundle findProvider(final IPackageImport pi, int options) {
+ final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ final ILicensePolicy policy = findPolicy(pi);
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ if (policy.accept(bundle)) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ for ( IPackageExport e : info.getExports() ) {
+ if ( pi.getPackageName().equals( e.getPackageName() ) && pi.getVersions().contains( e.getVersion() ) ) {
+ found.add( bundle );
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ };
+
+ accept( visitor, options );
+
+ return found.isEmpty() ? null : found.iterator().next();
+ }
+
+ public ISigilBundle findProvider(final IRequiredBundle req, int options) {
+ final ArrayList<ISigilBundle> found = new ArrayList<ISigilBundle>();
+
+ final ILicensePolicy policy = findPolicy(req);
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+
+ public boolean visit(ISigilBundle bundle) {
+ if (policy.accept(bundle)) {
+ IBundleModelElement info = bundle.getBundleInfo();
+ if ( req.getSymbolicName().equals( info.getSymbolicName() ) && req.getVersions().contains( info.getVersion() ) ) {
+ found.add( bundle );
+ return false;
+ }
+ }
+ return true;
+ }
+
+ };
+
+ accept( visitor, options );
+
+ return found.isEmpty() ? null : found.iterator().next();
+ }
+
+ public IBundleModelElement buildBundleModelElement(Manifest mf) {
+ IBundleModelElement info = null;
+
+ if ( mf != null ) {
+ Attributes attrs = mf.getMainAttributes();
+ String name = attrs.getValue("Bundle-SymbolicName");
+ if (name == null) {
+ // framework.jar doesn't have Bundle-SymbolicName!
+ name = attrs.getValue("Bundle-Name");
+ }
+
+ if (name != null) {
+ try {
+ info = ModelElementFactory.getInstance().newModelElement( IBundleModelElement.class );
+ info.setSymbolicName( name.split(";")[0] );
+ info.setVersion( Version.parseVersion( attrs.getValue( "Bundle-Version" ) ) );
+ info.setName( attrs.getValue( "Bundle-Name" ) );
+ info.setDescription( attrs.getValue( "Bundle-Description" ) );
+ info.setVendor( attrs.getValue( "Bundle-Vendor" ) );
+
+ String importStr = attrs.getValue( "Import-Package" );
+ if ( importStr != null ) {
+ addImports( info, importStr );
+ }
+ String exportStr = attrs.getValue( "Export-Package" );
+ if ( exportStr != null ) {
+ addExports( info, exportStr );
+ }
+
+ String reqStr = attrs.getValue( "Require-Bundle" );
+ if ( reqStr != null ) {
+ addRequires( info, reqStr );
+ }
+
+ String cpStr = attrs.getValue( "Bundle-Classpath" );
+
+ if ( cpStr != null ) {
+ addClasspath( info, cpStr );
+ }
+ }
+ catch (RuntimeException e) {
+ BldCore.error( "Failed to read info from bundle " + name, e );
+ // clear elements as clearly got garbage
+ info = null;
+ }
+ }
+ }
+
+ return info;
+ }
+
+ protected ILicensePolicy findPolicy(IModelElement elem) {
+ ILicenseManager man = BldCore.getLicenseManager();
+
+/* ISigilProjectModel p = elem.getAncestor(ISigilProjectModel.class);
+
+ ILicensePolicy policy = null;
+
+ if ( p != null ) {
+ policy = man.getPolicy(p);
+ }
+ else {
+ policy = man.getDefaultPolicy();
+ }
+
+ return policy; */
+
+ return man.getDefaultPolicy();
+ }
+
+ private void addClasspath(IBundleModelElement info, String cpStr) {
+ for ( String cp : cpStr.split( "," ) ) {
+ info.addClasspath( cp );
+ }
+ }
+
+ private void addExports(IBundleModelElement info, String exportStr) throws ModelElementFactoryException {
+ for ( String exp : QuoteUtil.split( exportStr ) ) {
+ try {
+ String[] parts = exp.split( ";" );
+ IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pe.setPackageName( parts[0].trim() );
+
+ if ( parts.length > 1 ) {
+ for (int i = 1; i < parts.length; i++ ) {
+ String check = parts[i];
+ if ( check.toLowerCase().startsWith( "version=" ) ) {
+ pe.setVersion( parseVersion(check.substring("version=".length())));
+ }
+ else if ( check.toLowerCase().startsWith( "specification-version=" ) ) {
+ pe.setVersion( parseVersion( check.substring("specification-version=".length()) ) );
+ }
+ else if ( check.toLowerCase().startsWith( "uses:=" ) ) {
+ for (String use : parseUses( check.substring( "uses:=".length() ) ) ) {
+ pe.addUse(use);
+ }
+ }
+ }
+ }
+ info.addExport(pe);
+ }
+ catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private Collection<String> parseUses(String uses) {
+ if ( uses.startsWith( "\"") ) {
+ uses = uses.substring(1, uses.length() - 2 );
+ }
+
+ return Arrays.asList( uses.split(",") );
+ }
+
+ private Version parseVersion(String val) {
+ val = val.replaceAll("\"", "");
+ return new Version(val);
+ }
+
+ private void addImports(IBundleModelElement info, String importStr) throws ModelElementFactoryException {
+ for ( String imp : QuoteUtil.split( importStr ) ) {
+ String[] parts = imp.split( ";" );
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName( parts[0].trim() );
+
+ if ( parts.length > 1 ) {
+ for ( int i = 1; i < parts.length; i++ ) {
+ String p = parts[i];
+ if ( p.toLowerCase().startsWith( "version=" ) ) {
+ pi.setVersions( VersionRange.parseVersionRange(p.substring("version=".length())));
+ }
+ else if ( p.toLowerCase().startsWith( "specification-version=" ) ) {
+ pi.setVersions( VersionRange.parseVersionRange( p.substring("specification-version=".length()) ));
+ }
+ else if ( p.toLowerCase().startsWith( "resolution:=" ) ) {
+ pi.setOptional( p.toLowerCase().substring("resolution:=".length()).equals( "optional") );
+ }
+ }
+ }
+ info.addImport(pi);
+ }
+ }
+
+ private void addRequires(IBundleModelElement info, String reqStr) throws ModelElementFactoryException {
+ for ( String imp : QuoteUtil.split( reqStr ) ) {
+ String[] parts = imp.split( ";" );
+ IRequiredBundle req = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+ req.setSymbolicName( parts[0] );
+
+ if ( parts.length > 1 ) {
+ if ( parts[1].toLowerCase().startsWith( "version=" ) ) {
+ req.setVersions( VersionRange.parseVersionRange(parts[1].substring("version=".length())));
+ }
+ else if ( parts[1].toLowerCase().startsWith( "specification-version=" ) ) {
+ req.setVersions( VersionRange.parseVersionRange( parts[1].substring("specification-version=".length()) ));
+ }
+ }
+ info.addRequiredBundle(req);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java
new file mode 100644
index 0000000..a4d09e8
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/AbstractRepositoryManager.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.cauldron.bld.core.repository.BundleResolver;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.RepositoryChangeEvent.Type;
+
+public abstract class AbstractRepositoryManager implements IRepositoryManager, IBundleRepositoryListener {
+
+ private HashSet<IRepositoryChangeListener> listeners = new HashSet<IRepositoryChangeListener>();
+
+ private boolean initialised;
+
+ private HashMap<String, IBundleRepository> repositories = new HashMap<String, IBundleRepository>();
+ private ArrayList<IBundleRepository> order = new ArrayList<IBundleRepository>();
+ private TreeMap<Integer, HashSet<IBundleRepository>> levelMap = new TreeMap<Integer, HashSet<IBundleRepository>>();
+ private int[] levels;
+
+ private BundleResolver resolver = new BundleResolver(this);
+
+ private ArrayList<ILibrary> libraries = new ArrayList<ILibrary>();
+
+ public void initialise() {
+ synchronized( repositories ) {
+ if ( !initialised ) {
+ initialised = true;
+ loadRepositories();
+ }
+ }
+ }
+
+ protected abstract void loadRepositories();
+
+ public void addRepositoryChangeListener(IRepositoryChangeListener listener) {
+ synchronized(listeners) {
+ listeners.add(listener);
+ }
+ }
+
+ public void removeRepositoryChangeListener(IRepositoryChangeListener listener) {
+ synchronized(listeners) {
+ listeners.remove(listener);
+ }
+ }
+
+ public void notifyChange(IBundleRepository repository) {
+ notifyListeners( new RepositoryChangeEvent(repository, Type.CHANGED ) );
+ }
+
+ private void notifyListeners(RepositoryChangeEvent event) {
+ ArrayList<IRepositoryChangeListener> safe = null;
+ synchronized(listeners) {
+ safe = new ArrayList<IRepositoryChangeListener>(listeners);
+ }
+ for ( IRepositoryChangeListener l : safe ) {
+ l.repositoryChanged(event);
+ }
+ }
+
+ protected void setRepositories(IBundleRepository[] repos) {
+ synchronized( repositories ) {
+ repositories.clear();
+ order.clear();
+ levelMap.clear();
+ resetLevels();
+ if ( repos != null ) {
+ for ( int i = 0; i < repos.length; i++ ) {
+ addRepository(repos[i], i);
+ }
+ }
+ }
+ }
+
+ protected void addRepository(IBundleRepository rep, int level) {
+ Type type = null;
+
+ synchronized( repositories ) {
+ IBundleRepository old = repositories.put(rep.getId(), rep);
+ if ( old == null ) {
+ type = Type.ADDED;
+ rep.addBundleRepositoryListener(this);
+ }
+ else {
+ old.removeBundleRepositoryListener(this);
+ type = Type.CHANGED;
+ order.remove(old);
+ clearLevel(rep);
+ }
+
+ order.add(rep);
+
+ HashSet<IBundleRepository> set = levelMap.get(level);
+
+ if ( set == null ) {
+ set = new HashSet<IBundleRepository>();
+ levelMap.put( level, set );
+ }
+
+ set.add( rep );
+ resetLevels();
+ }
+
+ notifyListeners( new RepositoryChangeEvent(rep, type ) );
+ }
+
+ protected void removeRepository(IBundleRepository rep) {
+ Type type = null;
+
+ synchronized( repositories ) {
+ if ( repositories.remove(rep.getId()) != null ) {
+ order.remove(rep);
+ type = Type.REMOVED;
+ clearLevel(rep);
+ resetLevels();
+ }
+ }
+
+ if ( type != null ) {
+ notifyListeners( new RepositoryChangeEvent(rep, type ) );
+ }
+ }
+
+ private void clearLevel(IBundleRepository rep) {
+ for ( Iterator<Map.Entry<Integer, HashSet<IBundleRepository>>> iter = levelMap.entrySet().iterator(); iter.hasNext(); ) {
+ Map.Entry<Integer, HashSet<IBundleRepository>> e = iter.next();
+ if ( e.getValue().remove(rep) ) {
+ if ( e.getValue().isEmpty() ) {
+ iter.remove();
+ }
+ break;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.repository.IRepositoryManager#getRepositories()
+ */
+ public Collection<IBundleRepository> getRepositories() {
+ initialise();
+ ArrayList<IBundleRepository> safe = null;
+
+ synchronized( repositories ) {
+ safe = new ArrayList<IBundleRepository>( order );
+ }
+
+ return safe;
+ }
+
+ private void resetLevels() {
+ Collections.sort(order, new Comparator<IBundleRepository>() {
+ public int compare(IBundleRepository o1, IBundleRepository o2) {
+ int l1 = findLevel(o1);
+ int l2 = findLevel(o2);
+
+ if ( l1 < l2 ) {
+ return -1;
+ }
+ else if ( l1 > l2 ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ private int findLevel(IBundleRepository rep) {
+ for ( Map.Entry<Integer, HashSet<IBundleRepository>> e : levelMap.entrySet() ) {
+ if ( e.getValue().contains( rep ) ) {
+ return e.getKey();
+ }
+ }
+ throw new IllegalStateException();
+ }
+ });
+ levels = new int[levelMap.size()];
+ int i = 0;
+ for ( Integer v : levelMap.keySet() ) {
+ levels[i++] = v;
+ }
+ }
+
+
+ public int[] getPriorityLevels() {
+ initialise();
+ synchronized( repositories ) {
+ return levels;
+ }
+ }
+
+ public Collection<IBundleRepository> getRepositories(int priorityLevel) {
+ initialise();
+ List<IBundleRepository> found = null;
+
+ synchronized (repositories) {
+ HashSet<IBundleRepository> b = levelMap.get(priorityLevel);
+ if ( b == null ) {
+ found = Collections.emptyList();
+ }
+ else {
+ found = new ArrayList<IBundleRepository>(b);
+ }
+ }
+
+ return found;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.repository.IRepositoryManager#addLibrary(org.cauldron.sigil.model.eclipse.ILibrary)
+ */
+ public void addLibrary(ILibrary library) {
+ synchronized( libraries ) {
+ libraries.add(library);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.repository.IRepositoryManager#removeLibrary(org.cauldron.sigil.model.eclipse.ILibrary)
+ */
+ public void removeLibrary(ILibrary library) {
+ synchronized( libraries ) {
+ libraries.remove(library);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.repository.IRepositoryManager#getLibraries()
+ */
+ public Collection<ILibrary> getLibraries() {
+ synchronized( libraries ) {
+ return libraries;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.internal.repository.IRepositoryManager#resolveLibrary(org.cauldron.sigil.model.eclipse.ILibraryImport)
+ */
+ public ILibrary resolveLibrary(final ILibraryImport l) {
+ final ArrayList<ILibrary> found = new ArrayList<ILibrary>(1);
+ //ISigilProjectModel p = l.getAncestor(ISigilProjectModel.class);
+ //
+ //IModelWalker w = new IModelWalker() {
+ // public boolean visit(IModelElement element) {
+ // if ( element instanceof ILibrary ) {
+ // updateLibrary(l, (ILibrary) element, found);
+ // return false;
+ // }
+ //
+ // return true;
+ // }
+ //};
+
+ //p.visit( w );
+
+ //if ( found.isEmpty() ) { // no project specific libraries - check workspace definitions
+ synchronized( libraries ) {
+ for ( ILibrary lib : libraries ) {
+ if ( l.getLibraryName().equals( lib.getName() ) && l.getVersions().contains(lib.getVersion()) ) {
+ updateLibrary(l, lib, found);
+ }
+ }
+ }
+ //}
+
+ return found.isEmpty() ? null : found.get(0);
+ }
+
+ protected void updateLibrary(ILibraryImport li, ILibrary l, ArrayList<ILibrary> found) {
+ if ( li.getLibraryName().equals( l.getName() ) && li.getVersions().contains(l.getVersion()) ) {
+ if ( found.isEmpty() ) {
+ found.add( l );
+ }
+ else {
+ ILibrary c = found.get(0);
+ if ( l.getVersion().compareTo(c.getVersion()) > 0 ) {
+ found.remove(0);
+ found.add(l);
+ }
+ }
+ }
+ }
+
+ public IBundleResolver getBundleResolver() {
+ return resolver;
+ }
+
+ public void visit(final IModelWalker walker) {
+ for (IBundleRepository rep : getRepositories()) {
+ IRepositoryVisitor wrapper = new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ bundle.visit(walker);
+ // return true as still want to visit other bundles
+ return true;
+ }
+ };
+ rep.accept(wrapper);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java
new file mode 100644
index 0000000..ed7ddc1
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepository.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collection;
+
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IBundleRepository {
+ static final int NORMAL_PRIORITY = 0;
+ static final int MAXIMUM_PRIORITY = -500;
+ static final int MINIMUM_PRIORITY = 500;
+
+ String getId();
+
+ void addBundleRepositoryListener(IBundleRepositoryListener listener);
+
+ void removeBundleRepositoryListener(IBundleRepositoryListener listener);
+
+ void accept(IRepositoryVisitor visitor);
+
+ void accept(IRepositoryVisitor visitor, int options);
+
+ void writeOBR(OutputStream out) throws IOException;
+
+ void refresh();
+
+ ISigilBundle findProvider(IPackageImport packageImport, int options);
+
+ ISigilBundle findProvider(IRequiredBundle bundle, int options);
+
+ Collection<ISigilBundle> findAllProviders(IRequiredBundle bundle, int options);
+
+ Collection<ISigilBundle> findAllProviders(IPackageImport packageImport, int options);
+
+ Collection<ISigilBundle> findProviders(ILibrary library, int options);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java
new file mode 100644
index 0000000..33c8e6c
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleRepositoryListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IBundleRepositoryListener {
+ void notifyChange(IBundleRepository repository);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java
new file mode 100644
index 0000000..f42d372
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IBundleResolver.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public interface IBundleResolver {
+ IResolution resolve(IModelElement element, ResolutionConfig config, IResolutionMonitor monitor) throws ResolutionException;
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java
new file mode 100644
index 0000000..7f2eba3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IProviderChangeListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IProviderChangeListener {
+ void notifyChange(IRepositoryProvider provider);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java
new file mode 100644
index 0000000..f8964f3
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryChangeListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public interface IRepositoryChangeListener {
+ void repositoryChanged(RepositoryChangeEvent event);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java
new file mode 100644
index 0000000..f4a5ede
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryManager.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.eclipse.core.runtime.CoreException;
+
+public interface IRepositoryManager {
+ void addRepositoryChangeListener(IRepositoryChangeListener listener);
+
+ void removeRepositoryChangeListener(IRepositoryChangeListener listener);
+
+ Collection<IBundleRepository> getRepositories();
+
+ Collection<IBundleRepository> getRepositories(int level);
+
+ void addLibrary(ILibrary library);
+
+ void removeLibrary(ILibrary library);
+
+ Collection<ILibrary> getLibraries();
+
+ ILibrary resolveLibrary(final ILibraryImport l);
+
+ IBundleResolver getBundleResolver();
+
+ int[] getPriorityLevels();
+
+ void visit(IModelWalker modelWalker);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java
new file mode 100644
index 0000000..856a8b4
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryProvider.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.Properties;
+
+public interface IRepositoryProvider {
+ IBundleRepository createRepository(String id, Properties properties) throws RepositoryException;
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java
new file mode 100644
index 0000000..1768815
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IRepositoryVisitor.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IRepositoryVisitor {
+ /**
+ * Visit the next bundle in the repository.
+ * Return true if should continue visiting other bundles, false otherwise.
+ * @param bundle
+ * @return
+ */
+ boolean visit(ISigilBundle bundle);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java
new file mode 100644
index 0000000..51360c7
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolution.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IResolution {
+ Set<ISigilBundle> getBundles();
+ ISigilBundle getProvider(IModelElement requirement);
+ List<IModelElement> getMatchedRequirements(ISigilBundle bundle);
+ void synchronize(IProgressMonitor monitor);
+ boolean isSynchronized();
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java
new file mode 100644
index 0000000..9a739c2
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/IResolutionMonitor.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface IResolutionMonitor {
+ boolean isCanceled();
+ void startResolution(IModelElement requirement);
+ void endResolution(IModelElement requirement, ISigilBundle sigilBundle);
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java
new file mode 100644
index 0000000..476b219
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryChangeEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class RepositoryChangeEvent {
+ public static enum Type { ADDED, CHANGED, REMOVED };
+
+ private Type type;
+ private IBundleRepository repository;
+
+ public RepositoryChangeEvent(IBundleRepository repository, Type type) {
+ this.repository = repository;
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+ public IBundleRepository getRepository() {
+ return repository;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java
new file mode 100644
index 0000000..a9a71c5
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/RepositoryException.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class RepositoryException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public RepositoryException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public RepositoryException(String message) {
+ super(message);
+ }
+
+ public RepositoryException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java
new file mode 100644
index 0000000..ae90a75
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionConfig.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+public class ResolutionConfig {
+ private int options;
+
+ public static final int INCLUDE_DEPENDENTS = 1;
+ public static final int INCLUDE_OPTIONAL = 2;
+ public static final int IGNORE_ERRORS = 4;
+ /** Return only bundles that are indexed locally */
+ public static final int INDEXED_ONLY = 8;
+ /** Return only bundles that are stored or cached locally */
+ public static final int LOCAL_ONLY = 16;
+
+ public ResolutionConfig() {
+ this(INCLUDE_DEPENDENTS);
+ }
+
+ public ResolutionConfig(int options) {
+ this.options = options;
+ }
+
+ public int getOptions() {
+ return options;
+ }
+
+ public boolean isDependents() {
+ return (options & INCLUDE_DEPENDENTS) != 0;
+ }
+
+ public boolean isIgnoreErrors() {
+ return (options & IGNORE_ERRORS) != 0;
+ }
+
+ public boolean isOptional() {
+ return (options & INCLUDE_OPTIONAL) != 0;
+ }
+
+ public boolean isCalculateLocalDependencies() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java
new file mode 100644
index 0000000..c0c093d
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionException.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.cauldron.sigil.model.IModelElement;
+
+public class ResolutionException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ private IModelElement[] parsed;
+
+ public ResolutionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ResolutionException(String message) {
+ super(message);
+ }
+
+ public ResolutionException(Throwable cause) {
+ super(cause);
+ }
+
+ public ResolutionException(IModelElement root, IModelElement[] parsed) {
+ super(buildMessage(root, parsed));
+ this.parsed = parsed;
+ }
+
+ private static String buildMessage(IModelElement root, IModelElement[] parsed) {
+ StringBuilder b = new StringBuilder();
+ b.append( "Failed to resolve " );
+ b.append( root );
+
+ if ( parsed.length > 0 ) {
+ b.append( " due to missing provider for " );
+ b.append( parsed[parsed.length - 1] );
+ }
+
+ return b.toString();
+ }
+
+ public IModelElement[] getParsed() {
+ return parsed;
+ }
+}
diff --git a/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java
new file mode 100644
index 0000000..06348f9
--- /dev/null
+++ b/sigil/org.cauldron.bld.core/src/org/cauldron/sigil/repository/ResolutionMonitorAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.repository;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public class ResolutionMonitorAdapter implements IResolutionMonitor {
+
+ private IProgressMonitor monitor;
+
+ public ResolutionMonitorAdapter(IProgressMonitor monitor) {
+ this.monitor = monitor;
+ }
+
+ public boolean isCanceled() {
+ return monitor.isCanceled();
+ }
+
+ public void startResolution(IModelElement requirement) {
+ monitor.subTask( "Resolving " + requirement);
+ }
+
+ public void endResolution(IModelElement requirement, ISigilBundle provider) {
+ monitor.subTask( (provider == null ? "Failed to resolve " : "Resolved ") + requirement);
+ }
+
+}
diff --git a/sigil/org.cauldron.bld.obr/.classpath b/sigil/org.cauldron.bld.obr/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.bld.obr/.project b/sigil/org.cauldron.bld.obr/.project
new file mode 100644
index 0000000..e4f71db
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.bld.obr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..cd02003
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Wed Sep 10 14:14:56 BST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs
new file mode 100644
index 0000000..66d1746
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,5 @@
+#Wed Sep 10 14:14:56 BST 2008
+eclipse.preferences.version=1
+pluginProject.equinox=false
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF b/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..20dfeea
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Obr Plug-in
+Bundle-SymbolicName: org.cauldron.bld.obr
+Bundle-Version: 0.8.0.qualifier
+Bundle-Vendor: Paremus Ltd
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Require-Bundle: org.cauldron.bld.core;bundle-version="0.6.0",
+ org.eclipse.equinox.common;bundle-version="3.4.0"
+Import-Package: org.osgi.framework;version="1.4.0"
+Export-Package: org.cauldron.bld.obr
diff --git a/sigil/org.cauldron.bld.obr/build.properties b/sigil/org.cauldron.bld.obr/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java
new file mode 100644
index 0000000..71c033d
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/AbstractOBRBundleRepository.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+
+public abstract class AbstractOBRBundleRepository extends AbstractBundleRepository {
+ private static SAXParserFactory factory = SAXParserFactory.newInstance();
+
+ private URL obrURL;
+ private File obrlCache;
+ private File bundleCache;
+ private long updatePeriod;
+
+ public AbstractOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+ super(id);
+ this.obrURL = repositoryURL;
+ this.obrlCache = obrCache;
+ this.bundleCache = bundleCache;
+ this.updatePeriod = updatePeriod;
+ }
+
+ public void refresh() {
+ obrlCache.delete();
+ }
+
+ protected void readBundles(OBRListener listener) throws Exception {
+ syncOBRIndex();
+ OBRHandler handler = new OBRHandler(getObrURL(), getBundleCache(), listener);
+ SAXParser parser = factory.newSAXParser();
+ parser.parse(getObrlCache(), handler );
+ }
+
+ private void syncOBRIndex() {
+ if ( isUpdated() ) {
+ InputStream in = null;
+ OutputStream out = null;
+
+ try {
+ URLConnection c = getObrURL().openConnection();
+ c.connect();
+ in = c.getInputStream();
+ out = new FileOutputStream(getObrlCache());
+ stream(in, out);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ getObrlCache().setLastModified(0);
+ }
+ finally {
+ close(in, out);
+ }
+ }
+ }
+
+ private void close(InputStream in, OutputStream out) {
+ if ( in != null ) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if ( out != null ) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void stream(InputStream in, OutputStream out) throws IOException {
+ byte[] buf = new byte[1024];
+ for(;;) {
+ int r = in.read(buf);
+ if ( r == -1 ) {
+ break;
+ }
+ out.write(buf, 0, r);
+ }
+ out.flush();
+ }
+
+ private boolean isUpdated() {
+ if ( !getObrlCache().exists() ) {
+ return true;
+ }
+
+ return getObrlCache().lastModified() + getUpdatePeriod() < System.currentTimeMillis();
+ }
+
+ public URL getObrURL() {
+ return obrURL;
+ }
+
+ public File getObrlCache() {
+ return obrlCache;
+ }
+
+ public File getBundleCache() {
+ return bundleCache;
+ }
+
+ public long getUpdatePeriod() {
+ return updatePeriod;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java
new file mode 100644
index 0000000..f0ed825
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/CachingOBRBundleRepository.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.lang.ref.SoftReference;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+public class CachingOBRBundleRepository extends AbstractOBRBundleRepository {
+
+ private SoftReference<List<ISigilBundle>> bundles;
+
+ public CachingOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+ super(id, repositoryURL, obrCache, bundleCache, updatePeriod);
+ }
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ for ( ISigilBundle b : loadFromCache(options) ) {
+ if ( !visitor.visit(b) ) {
+ break;
+ }
+ }
+ }
+
+ public synchronized void refresh() {
+ super.refresh();
+ if ( bundles != null ) {
+ bundles.clear();
+ notifyChange();
+ }
+ }
+
+ private synchronized List<ISigilBundle> loadFromCache(int options) {
+ List<ISigilBundle> cached = bundles == null ? null : bundles.get();
+ if ( cached == null ) {
+ try {
+ final LinkedList<ISigilBundle> read = new LinkedList<ISigilBundle>();
+ readBundles(new OBRListener() {
+ public void handleBundle(ISigilBundle bundle) {
+ read.add(bundle);
+ }
+ });
+ cached = read;
+ bundles = new SoftReference<List<ISigilBundle>>(cached);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ return cached;
+ }
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java
new file mode 100644
index 0000000..4c11840
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/NonCachingOBRBundleRepository.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.URL;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+
+public class NonCachingOBRBundleRepository extends AbstractOBRBundleRepository {
+
+ public static void main(String[] args) throws Exception {
+ String url = args[0];
+ String obr = args[1];
+ String cache = args[2];
+ String update = args[3];
+ BldCore.init();
+ NonCachingOBRBundleRepository rep = new NonCachingOBRBundleRepository( "main", new URL(url), new File(obr), new File(cache), Long.parseLong(update));
+ rep.accept(new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ System.out.println( "Found " + bundle );
+ return true;
+ }
+ });
+ }
+
+ public NonCachingOBRBundleRepository(String id, URL repositoryURL, File obrCache, File bundleCache, long updatePeriod) {
+ super(id, repositoryURL, obrCache, bundleCache, updatePeriod);
+ }
+
+ @Override
+ public void accept(final IRepositoryVisitor visitor, int options) {
+ try {
+ readBundles(new OBRListener() {
+ boolean visit = true;
+ public void handleBundle(ISigilBundle bundle) {
+ if ( visit ) {
+ visit = visitor.visit(bundle);
+ }
+ }
+ });
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java
new file mode 100644
index 0000000..b090521
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRHandler.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.LDAPExpr;
+import org.cauldron.sigil.model.common.LDAPParseException;
+import org.cauldron.sigil.model.common.LDAPParser;
+import org.cauldron.sigil.model.common.SimpleTerm;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Version;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+final class OBRHandler extends DefaultHandler {
+ private static final String PACKAGE = "package";
+ private static final String URI = "uri";
+ private static final String PRESENTATION_NAME = "presentationname";
+ private static final String VERSION = "version";
+ private static final String SYMBOLIC_NAME = "symbolicname";
+ private final File cacheDir;
+ private final URL obrURL;
+ private final OBRListener listener;
+
+ private HashSet<String> sanity = new HashSet<String>();
+ private Locator locator;
+ private ISigilBundle bundle;
+ private IPackageExport export;
+
+ public OBRHandler(URL obrURL, File bundleCache, OBRListener listener) {
+ this.obrURL = obrURL;
+ this.cacheDir = bundleCache;
+ this.listener = listener;
+ }
+
+ public void setDocumentLocator (Locator locator) {
+ this.locator = locator;
+ }
+
+ public void startElement (String uri, String localName,
+ String qName, Attributes attributes) throws SAXException {
+ if ( "resource".equals( qName ) ) {
+ startResource(attributes);
+ }
+ else if ( "capability".equals( qName ) ) {
+ startCapability(attributes);
+ }
+ else if( "require".equals( qName ) ) {
+ startRequire(attributes);
+ }
+ else if ( "p".equals( qName ) ) {
+ startProperty(attributes);
+ }
+ }
+
+ public void endElement (String uri, String localName, String qName)
+ throws SAXException {
+ if ( "resource".equals( qName ) ) {
+ endResource();
+ }
+ else if ( "capability".equals( qName ) ) {
+ endCapability();
+ }
+ else if( "require".equals( qName ) ) {
+ endRequire();
+ }
+ else if ( "p".equals( qName ) ) {
+ endProperty();
+ }
+ }
+
+ private void startResource(Attributes attributes) throws SAXException {
+ try {
+ String uri = attributes.getValue("", URI);
+ if ( uri.endsWith( ".jar" ) ) {
+ if ( !sanity.add(uri) ) {
+ throw new RuntimeException(uri);
+ }
+ ISigilBundle b = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+ IBundleModelElement info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class);
+ info.setSymbolicName(attributes.getValue("", SYMBOLIC_NAME));
+ info.setVersion(new Version(attributes.getValue("", VERSION)));
+ info.setName(attributes.getValue("", PRESENTATION_NAME));
+ URI l = makeAbsolute(uri);
+ info.setUpdateLocation(l);
+ b.setLocation(cachePath(info));
+ b.setBundleInfo(info);
+ bundle = b;
+ }
+ }
+ catch (Exception e) {
+ throw new SAXParseException("Failed to build bundle info", locator, e);
+ }
+ }
+
+ private URI makeAbsolute(String uri) throws URISyntaxException {
+ URI l = new URI(uri);
+ if ( !l.isAbsolute() ) {
+ String base = obrURL.toExternalForm();
+ int i = base.lastIndexOf("/");
+ if ( i != -1 ) {
+ base = base.substring(0, i);
+ l = new URI( base + (uri.startsWith("/") ? "" : "/") + uri );
+ }
+ }
+ return l;
+ }
+
+ private IPath cachePath(IBundleModelElement info) {
+ return new Path(cacheDir.getAbsolutePath()).append( info.getSymbolicName() + "_" + info.getVersion() + ".jar" );
+ }
+
+ private void startCapability(Attributes attributes) {
+ if ( bundle != null ) {
+ if ( PACKAGE.equals( attributes.getValue("", "name") ) ) {
+ export = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ }
+ }
+ }
+
+ private void startRequire(Attributes attributes) throws SAXParseException {
+ if ( bundle != null ) {
+ String name = attributes.getValue("name");
+ if ( PACKAGE.equals( name ) ) {
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ try {
+ LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") );
+ pi.setPackageName(decodePackage(expr, locator));
+ pi.setVersions(decodeVersions(expr, locator));
+ pi.setOptional(Boolean.valueOf(attributes.getValue("optional")));
+ bundle.getBundleInfo().addImport(pi);
+ } catch (LDAPParseException e) {
+ throw new SAXParseException("Failed to parse filter", locator, e);
+ }
+ }
+ else if ( "bundle".equals( name ) ) {
+ IRequiredBundle b = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+ try {
+ LDAPExpr expr = LDAPParser.parseExpression(attributes.getValue("filter") );
+ b.setSymbolicName(decodeSymbolicName(expr, locator));
+ b.setVersions(decodeVersions(expr, locator));
+ b.setOptional(Boolean.valueOf(attributes.getValue("optional")));
+ bundle.getBundleInfo().addRequiredBundle(b);
+ } catch (Exception e) {
+ System.err.println( "Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName() );
+ throw new SAXParseException("Failed to parse filter in bundle " + bundle.getBundleInfo().getSymbolicName(), locator, e);
+ }
+ }
+ //else if ( "ee".equals( name ) ) {
+ // TODO ignore for now...
+ //}
+ //else if ( "service".equals( name ) ) {
+ // TODO ignore for now...
+ //}
+ //else {
+ // for ( int i = 0; i < attributes.getLength(); i++ ) {
+ // System.out.println( "Found requirement " + attributes.getValue(i) );
+ // }
+ //}
+ }
+ }
+
+ private static VersionRange decodeVersions(LDAPExpr expr, Locator locator) throws SAXParseException {
+ try {
+ return VersionRangeHelper.decodeVersions(expr);
+ }
+ catch (NumberFormatException e) {
+ throw new SAXParseException(e.getMessage(), locator);
+ }
+ }
+
+ private void startProperty(Attributes attributes) {
+ if ( export != null ) {
+ String name = attributes.getValue("", "n");
+ String value = attributes.getValue("", "v");
+ if ( PACKAGE.equals( name ) ) {
+ export.setPackageName(value);
+ }
+ else if ( "uses".equals( name ) ) {
+ String[] split = value.split(",");
+ export.setUses( Arrays.asList(split) );
+ }
+ else if ( "version".equals( name ) ) {
+ export.setVersion( new Version(value) );
+ }
+ }
+ }
+
+ private void endResource() {
+ if ( bundle != null ) {
+ listener.handleBundle(bundle);
+ bundle = null;
+ }
+ }
+
+ private void endCapability() {
+ if ( bundle != null ) {
+ if ( export != null ) {
+ bundle.getBundleInfo().addExport(export);
+ export = null;
+ }
+ }
+ }
+
+ private void endRequire() {
+ // TODO Auto-generated method stub
+
+ }
+
+ private void endProperty() {
+ // TODO Auto-generated method stub
+
+ }
+
+ private static String decodePackage(LDAPExpr expr, Locator locator) throws SAXParseException {
+ ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1);
+
+ findTerms("package", expr, terms);
+
+ if ( terms.isEmpty() ) {
+ throw new SAXParseException("Missing name filter in " + expr, locator);
+ }
+
+ return terms.get(0).getRval();
+ }
+
+ private static String decodeSymbolicName(LDAPExpr expr, Locator locator) throws SAXParseException {
+ ArrayList<SimpleTerm> terms = new ArrayList<SimpleTerm>(1);
+
+ findTerms("symbolicname", expr, terms);
+
+ if ( terms.isEmpty() ) {
+ throw new SAXParseException("Missing name filter in " + expr, locator);
+ }
+
+ return terms.get(0).getRval();
+ }
+
+ private static void findTerms(String string, LDAPExpr expr, List<SimpleTerm> terms) throws SAXParseException {
+ if ( expr instanceof SimpleTerm ) {
+ SimpleTerm term = (SimpleTerm) expr;
+ if ( term.getName().equals(string) ) {
+ terms.add(term);
+ }
+ }
+ else {
+ for ( LDAPExpr c : expr.getChildren() ) {
+ findTerms(string, c, terms);
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java
new file mode 100644
index 0000000..ae7bdd2
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public interface OBRListener {
+ void handleBundle(ISigilBundle bundle);
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java
new file mode 100644
index 0000000..4653d38
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/OBRRepositoryProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+
+public class OBRRepositoryProvider implements IRepositoryProvider {
+ public IBundleRepository createRepository(String id, Properties preferences) throws RepositoryException {
+ try {
+ URL repositoryURL = new URL( preferences.getProperty("url") );
+ File indexCache = new File( preferences.getProperty("index") );
+ File localCache = new File( preferences.getProperty("cache") );
+ // TODO create user configurable updatePeriod
+ long updatePeriod = TimeUnit.MILLISECONDS.convert(60*60*24*7, TimeUnit.SECONDS);
+ if ( preferences.getProperty("inmemory") == null ) {
+ return new NonCachingOBRBundleRepository(id, repositoryURL, indexCache, localCache, updatePeriod);
+ }
+ else {
+ return new CachingOBRBundleRepository(id, repositoryURL, indexCache, localCache, updatePeriod);
+ }
+ }
+ catch (MalformedURLException e) {
+ throw new RepositoryException("Invalid repository url", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java
new file mode 100644
index 0000000..50b0fb1
--- /dev/null
+++ b/sigil/org.cauldron.bld.obr/src/org/cauldron/bld/obr/VersionRangeHelper.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.bld.obr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.model.common.LDAPExpr;
+import org.cauldron.sigil.model.common.LDAPParseException;
+import org.cauldron.sigil.model.common.LDAPParser;
+import org.cauldron.sigil.model.common.Not;
+import org.cauldron.sigil.model.common.Ops;
+import org.cauldron.sigil.model.common.SimpleTerm;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.osgi.framework.Version;
+
+class VersionRangeHelper {
+
+ // e.g. (&(version>=1.0.0)(version<=2.0.0)) (&(version>1.0.0)(version<2.0.0)) (&(!(version<1.0.0))(!(version>2.0.0))) (&(!(version<=1.0.0))(!(version>=2.0.0))) (version=1.0.0) (version>=1.0.0) (version<=2.0.0) (version>1.0.0) (version<2.0.0) (!(version>2.0.0)) (!(version<1.0.0)) (!(version>=2.0.0)) (!(version<=1.0.0))
+ public static void main(String[] args) throws LDAPParseException {
+ for ( String arg : args ) {
+ LDAPExpr expr = LDAPParser.parseExpression(arg.trim());
+ System.out.println( expr + " -> " + decodeVersions(expr) );
+ }
+ }
+
+ static VersionRange decodeVersions(LDAPExpr expr) throws NumberFormatException {
+ ArrayList<LDAPExpr> terms = new ArrayList<LDAPExpr>(1);
+
+ findExpr("version", expr, terms);
+
+ if ( terms.isEmpty() ) {
+ // woo hoo!
+ return VersionRange.ANY_VERSION;
+ }
+ else {
+ switch ( terms.size() ) {
+ case 1: {
+ return parseSimpleVersionRange(terms.get(0));
+ }
+ case 2: {
+ return parseCompoundVersionRange(terms.get(0), terms.get(1));
+ }
+ default: {
+ // (&(version>=min)(!(version=min))(version<=max)(!(version=max))) - (min,max) - not dealt with!!
+ // (&(|(version>min)(version=min))(|(version<max)(version=max))) - [min,max] - not dealt with!!
+ throw new NumberFormatException("Failed to parse complicated version expression " + expr);
+ }
+ }
+ }
+ }
+
+ // (&(version>=min)(version<=max)) - [min,max]
+ // (&(version>min)(version<max)) - (min,max)
+ //
+ // (&(!(version<min))(!(version>max))) - [min,max]
+ // (&(!(version<=min))(!(version>=max)) - (min,max)
+ private static VersionRange parseCompoundVersionRange(LDAPExpr left, LDAPExpr right) throws NumberFormatException {
+ VersionRange one = parseSimpleVersionRange(left);
+ VersionRange two = parseSimpleVersionRange(right);
+
+ // sanity check
+ if ( one.isPointVersion() || two.isPointVersion() ) {
+ throw new NumberFormatException("Unexpected point version in compound expression " + left);
+ }
+
+ VersionRange max = one.getFloor().equals( Version.emptyVersion ) ? one : two;
+ VersionRange min = max == one ? two : one;
+
+ return new VersionRange( min.isOpenFloor(), min.getFloor(), max.getCeiling(), max.isOpenCeiling() );
+ }
+
+ // possible variations
+ // (version=v) - [v,v]
+ //
+ // (version>=min) - [min,*)
+ // (version<=max) - [0,max]
+ //
+ // (version>min) - (min,*)
+ // (version<max) - [0,max)
+ //
+ // (!(version>max)) - [0,max]
+ // (!(version<min)) - [min,*)
+ // (!(version>=max)) - [0,max)
+ // (!(version<=min)) - (0,*)
+ private static VersionRange parseSimpleVersionRange(LDAPExpr expr) throws NumberFormatException {
+ Version min = Version.emptyVersion;
+ Version max = VersionRange.INFINITE_VERSION;
+ boolean openFloor = false;
+ boolean openCeiling = false;
+ if ( expr instanceof Not ) {
+ Not n = (Not) expr;
+ SimpleTerm t = (SimpleTerm) n.getEx();
+ if ( t.getOp() == Ops.EQ ) {
+ throw new NumberFormatException("Unexpected point version in negated expression " + expr);
+ }
+ if ( !isMax(t.getOp()) ) {
+ max = toVersion(t);
+ openCeiling = !openFloor(t);
+ }
+ else if ( !isMin(t.getOp()) ) {
+ min = toVersion(t);
+ openFloor = !openCeiling(t);
+ }
+ else {
+ throw new IllegalStateException("Unexpected operator " + t.getOp());
+ }
+ }
+ else {
+ SimpleTerm t = (SimpleTerm) expr;
+ if ( t.getOp().equals( Ops.EQ ) ) {
+ max = toVersion(t);
+ min = max;
+ openFloor = false;
+ openCeiling = false;
+ }
+ else if ( isMax(t.getOp()) ) {
+ max = toVersion(t);
+ openCeiling = openCeiling(t);
+ }
+ else if ( isMin(t.getOp()) ) {
+ min = toVersion(t);
+ openFloor = openFloor(t);
+ }
+ else {
+ throw new IllegalStateException("Unexpected operator " + t.getOp());
+ }
+ }
+
+ return new VersionRange( openFloor, min, max, openCeiling );
+ }
+
+ private static Version toVersion(SimpleTerm t) {
+ return new Version(t.getRval());
+ }
+
+ private static boolean isMax(Ops op) {
+ return op == Ops.LE || op == Ops.LT;
+ }
+
+ private static boolean isMin(Ops op) {
+ return op == Ops.GE || op == Ops.GT;
+ }
+
+ private static boolean openFloor(SimpleTerm t) {
+ return t.getOp() == Ops.GT;
+ }
+
+ private static boolean openCeiling(SimpleTerm t) {
+ return t.getOp() == Ops.LT;
+ }
+
+ private static void findExpr(String string, LDAPExpr expr, List<LDAPExpr> terms) {
+ if ( expr instanceof SimpleTerm ) {
+ SimpleTerm term = (SimpleTerm) expr;
+ if ( term.getName().equals(string) ) {
+ terms.add(term);
+ }
+ }
+ else if ( expr instanceof Not ) {
+ Not not = (Not) expr;
+ if ( not.getEx() instanceof SimpleTerm ) {
+ SimpleTerm term = (SimpleTerm) not.getEx();
+ if ( term.getName().equals(string) ) {
+ terms.add(not);
+ }
+ }
+ }
+ else {
+ for ( LDAPExpr c : expr.getChildren() ) {
+ findExpr(string, c, terms);
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.all.feature/.project b/sigil/org.cauldron.sigil.all.feature/.project
new file mode 100644
index 0000000..91235d4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.all.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.all.feature/build.properties b/sigil/org.cauldron.sigil.all.feature/build.properties
new file mode 100644
index 0000000..df096f5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/build.properties
@@ -0,0 +1,2 @@
+# Setting bin.includes to empty ensures no feature JAR is built
+# bin.includes=
diff --git a/sigil/org.cauldron.sigil.all.feature/feature.xml b/sigil/org.cauldron.sigil.all.feature/feature.xml
new file mode 100644
index 0000000..a486371
--- /dev/null
+++ b/sigil/org.cauldron.sigil.all.feature/feature.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<feature
+ id="org.cauldron.sigil.all.feature"
+ label="Sigil All Feature"
+ version="0.8.0">
+
+ <description url="http://www.example.com/description">
+ [Enter Feature Description here.]
+ </description>
+
+ <copyright url="http://www.apache.org/licenses/LICENSE-2.0">
+ [Enter Feature Description here.]
+ </copyright>
+
+ <license url="http://www.apache.org/licenses/LICENSE-2.0">
+ Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+ </license>
+
+ <includes
+ id="org.cauldron.sigil.feature"
+ version="0.0.0"/>
+
+ <includes
+ id="org.cauldron.sigil.obr.feature"
+ version="0.0.0"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.core/.classpath b/sigil/org.cauldron.sigil.core/.classpath
new file mode 100644
index 0000000..e26ebfb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.classpath
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="org/cauldron/newton/descriptor/**"/>
+ <accessrule kind="accessible" pattern="org/eclipse/pde/internal/**"/>
+ </accessrules>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="lib/commons-lang-2.4.jar" sourcepath="org.cauldron.sigil.coresrc.zip"/>
+ <classpathentry kind="output" path="build/classes"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.core/.project b/sigil/org.cauldron.sigil.core/.project
new file mode 100644
index 0000000..ed75ac8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ed4b35e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Tue Sep 16 15:53:19 BST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..f9bcf63
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/META-INF/MANIFEST.MF
@@ -0,0 +1,28 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: org.cauldron.sigil.core
+Bundle-SymbolicName: org.cauldron.sigil.core;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.SigilCore
+Bundle-Vendor: Paremus Ltd.
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.expressions,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.core,
+ org.eclipse.ui,
+ org.eclipse.ui.console;bundle-version="3.3.0",
+ org.cauldron.sigil.utils,
+ org.cauldron.bld.core
+Bundle-ActivationPolicy: lazy
+Export-Package: org.cauldron.sigil,
+ org.cauldron.sigil.install,
+ org.cauldron.sigil.job,
+ org.cauldron.sigil.model.project,
+ org.cauldron.sigil.model.repository,
+ org.cauldron.sigil.model.util,
+ org.cauldron.sigil.preferences
+Bundle-ClassPath: .,
+ lib/commons-lang-2.4.jar
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: junit.framework;version="3.8.2";resolution:=optional
diff --git a/sigil/org.cauldron.sigil.core/build.properties b/sigil/org.cauldron.sigil.core/build.properties
new file mode 100644
index 0000000..a462907
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/build.properties
@@ -0,0 +1,10 @@
+source.. = src/
+output.. = build/classes/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ lib/commons-lang-2.4.jar,\
+ schema/
+bin.excludes = META-INF/.svn/
+src.includes = src/,\
+ META-INF/
diff --git a/sigil/org.cauldron.sigil.core/plugin.xml b/sigil/org.cauldron.sigil.core/plugin.xml
new file mode 100644
index 0000000..3128c58
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/plugin.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="org.cauldron.sigil.repositoryprovider" name="Bundle Repository Provider" schema="schema/org.cauldron.sigil.repositoryprovider.exsd"/>
+ <extension-point id="org.cauldron.sigil.installbuilder" name="Newton Install Builder" schema="schema/org.cauldron.sigil.runtime.installbuilder.exsd"/>
+
+ <extension
+ point="org.eclipse.core.contenttype.contentTypes">
+ <content-type
+ base-type="org.eclipse.core.runtime.text"
+ file-extensions="script,nsh"
+ id="org.cauldron.sigil.content.NewtonScriptType"
+ name="Newton Script"
+ priority="normal">
+ </content-type>
+
+ </extension>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="org.cauldron.sigil.property.SigilPropertyTester"
+ id="org.cauldron.sigil.property.NewtonPropertyTester"
+ namespace="org.cauldron.sigil"
+ properties="isNewtonResource,isCompositeResource,isSystemResource,isScriptResource,isSigilProject"
+ type="org.eclipse.core.resources.IResource"/>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.adapters">
+ <factory
+ adaptableType="org.eclipse.core.resources.IFile"
+ class="org.cauldron.sigil.internal.adapter.FileAdaptorFactory">
+ <adapter type="org.cauldron.sigil.model.eclipse.ISigilBundle"/>
+ </factory>
+ <factory
+ adaptableType="org.eclipse.core.resources.IProject"
+ class="org.cauldron.sigil.internal.adapter.ProjectAdaptorFactory">
+ <adapter type="org.cauldron.sigil.model.project.ISigilProjectModel"/>
+ </factory>
+ </extension>
+ <extension
+ id="org.cauldron.sigil.core.sigilnature"
+ name="Sigil Nature"
+ point="org.eclipse.core.resources.natures">
+ <runtime>
+ <run class="org.cauldron.sigil.nature.SigilProjectNature"/>
+ </runtime>
+ </extension>
+ <extension
+ id="sigilBuilder"
+ name="Sigil Bundle Builder"
+ point="org.eclipse.core.resources.builders">
+ <builder
+ hasNature="false"
+ isConfigurable="false">
+ <run class="org.cauldron.sigil.internal.builders.SigilIncrementalProjectBuilder"/>
+ </builder>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.preferences">
+ <initializer
+ class="org.cauldron.sigil.preferences.SigilPreferencesInitializer">
+ </initializer>
+ </extension>
+ <extension
+ point="org.cauldron.sigil.repositoryprovider">
+ <provider
+ class="org.cauldron.sigil.internal.repository.eclipse.WorkspaceRepositoryProvider"
+ defaultLevel="-3"
+ dynamic="false"
+ id="org.cauldron.sigil.core.workspaceprovider"
+ type="Workspace Repository">
+ </provider>
+ <provider
+ class="org.cauldron.bld.core.repository.FileSystemRepositoryProvider"
+ dynamic="true"
+ id="org.cauldron.sigil.core.file"
+ type="File System Repository">
+ </provider>
+ </extension>
+ <extension
+ id="org.cauldron.sigil.unresolvedDependencyMarker"
+ name="Unresolved Dependency"
+ point="org.eclipse.core.resources.markers">
+ <persistent
+ value="true">
+ </persistent>
+ <super
+ type="org.eclipse.core.resources.problemmarker">
+ </super>
+ <attribute
+ name="element">
+ </attribute>
+ <attribute
+ name="versionRange">
+ </attribute>
+ </extension>
+ <extension
+ id="org.cauldron.sigil.unresolvedDependencyMarker.importPackage"
+ name="Unresolved Import Package"
+ point="org.eclipse.core.resources.markers">
+ <persistent
+ value="true">
+ </persistent>
+ <super
+ type="org.cauldron.sigil.unresolvedDependencyMarker">
+ </super>
+ </extension>
+ <extension
+ id="org.cauldron.sigil.unresolvedDependencyMarker.requireBundle"
+ name="Unresolve Require Bundle"
+ point="org.eclipse.core.resources.markers">
+ <persistent
+ value="true">
+ </persistent>
+ <super
+ type="org.cauldron.sigil.unresolvedDependencyMarker">
+ </super>
+ </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd
new file mode 100644
index 0000000..051c866
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.repositoryprovider.exsd
@@ -0,0 +1,158 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.cauldron.sigil.core" id="org.cauldron.sigil.repository" name="Sigil Bundle Repository Provider"/>
+ </appinfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="provider" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="provider">
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="type" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.cauldron.sigil.repository.IRepositoryProvider"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="icon" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="resource"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="dynamic" type="boolean" use="default" value="true">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="defaultLevel" type="string" use="default" value="500">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd
new file mode 100644
index 0000000..3522aa4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/schema/org.cauldron.sigil.runtime.installbuilder.exsd
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.runtime" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.cauldron.sigil.runtime" id="org.cauldron.sigil.runtime.installbuilder" name="Newton Install Builder"/>
+ </appinfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="builder" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="builder">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn=":org.cauldron.sigil.install.INewtonInstallBuilder"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="priority" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java
new file mode 100644
index 0000000..b0e123e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/SigilCore.java
@@ -0,0 +1,570 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.install.IOSGiInstallManager;
+import org.cauldron.sigil.internal.install.OSGiInstallManager;
+import org.cauldron.sigil.internal.model.project.SigilModelRoot;
+import org.cauldron.sigil.internal.model.project.SigilProject;
+import org.cauldron.sigil.internal.model.repository.RepositoryConfiguration;
+import org.cauldron.sigil.internal.repository.eclipse.GlobalRepositoryManager;
+import org.cauldron.sigil.internal.repository.eclipse.SigilRepositoryManager;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilModelRoot;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilCore extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.core";
+ public static final String NATURE_ID = PLUGIN_ID + ".sigilnature";
+ public static final String PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.SigilPreferencePage";
+ public static final String OSGI_INSTALLS_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.osgiInstalls";
+ public static final String EXCLUDED_RESOURCES_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.excludedResources";
+ public static final String REPOSITORIES_PREFERENCES_ID = "org.cauldron.sigil.ui.preferences.repositoriesPreferencePage";
+ public static final String OSGI_SCRIPT_TYPE = "org.cauldron.sigil.content.OSGiScriptType";
+ public static final String SIGIL_PROJECT_FILE = IBldProject.PROJECT_FILE;
+ public static final String BUILDER_ID = PLUGIN_ID + ".sigilBuilder";
+ public static final String CLASSPATH_CONTAINER_PATH = PLUGIN_ID + ".classpathContainer";
+
+ public static final String OSGI_INSTALLS = "org.cauldron.osgi.installs";
+ public static final String OSGI_DEFAULT_INSTALL_ID = "org.cauldron.osgi.default.install.id";
+ public static final String OSGI_INSTALL_PREFIX = "org.cauldron.osgi.install.";
+ public static final String OSGI_SOURCE_LOCATION = "org.cauldron.osgi.source.location";
+ public static final String OSGI_INSTALL_CHECK_PREFERENCE = "org.cauldron.osgi.install.check";
+ public static final String LIBRARY_KEYS_PREF = "org.cauldron.osgi.library.keys";
+ public static final String PREFERENCES_REBUILD_PROJECTS = "org.cauldron.sigil.rebuild.projects";
+ public static final String QUALIFY_VERSIONS = "org.cauldron.sigil.qualify.versions";
+
+ public static final String DEFAULT_VERSION_LOWER_BOUND = "org.cauldron.sigil.versionLowerBound";
+ public static final String DEFAULT_VERSION_UPPER_BOUND = "org.cauldron.sigil.versionUpperBound";
+
+ public static final String DEFAULT_EXCLUDED_RESOURCES = "org.cauldron.sigil.excludedResources";
+ public static final String PREFERENCES_NOASK_OSGI_INSTALL = "org.cauldron.sigil.noAskOSGIHome";
+ public static final String PREFERENCES_ADD_IMPORT_FOR_EXPORT = "org.cauldron.sigil.addImportForExport";
+ public static final String INCLUDE_OPTIONAL_DEPENDENCIES = "org.cauldron.sigil.includeOptionalDependencies";
+
+ public static final String INSTALL_BUILDER_EXTENSION_POINT_ID = "org.cauldron.sigil.installbuilder";
+ public static final String REPOSITORY_PROVIDER_EXTENSION_POINT_ID = "org.cauldron.sigil.repositoryprovider";
+
+ public static final String MARKER_UNRESOLVED_DEPENDENCY = "org.cauldron.sigil.unresolvedDependencyMarker";
+ public static final String MARKER_UNRESOLVED_IMPORT_PACKAGE = "org.cauldron.sigil.unresolvedDependencyMarker.importPackage";
+ public static final String MARKER_UNRESOLVED_REQUIRE_BUNDLE = "org.cauldron.sigil.unresolvedDependencyMarker.requireBundle";
+ public static final String REPOSITORY_SET = PLUGIN_ID + ".repository.set";
+ public static final String PREFERENCES_INCLUDE_OPTIONAL = PLUGIN_ID + ".include.optional";
+
+ private static final Object NULL = new Object();
+
+ // The shared instance
+ private static SigilCore plugin;
+
+ private ServiceTracker descriptorTracker;
+ private ServiceTracker registryTracker;
+ private ServiceTracker serializerTracker;
+
+ private static IRepositoryConfiguration repositoryConfig;
+ private static OSGiInstallManager installs;
+ private static ISigilModelRoot modelRoot;
+ private static HashMap<Object, SigilRepositoryManager> repositoryManagers = new HashMap<Object, SigilRepositoryManager>();
+ private static GlobalRepositoryManager globalRepositoryManager;
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static SigilCore getDefault() {
+ return plugin;
+ }
+
+ public static CoreException newCoreException(String msg, Throwable t) {
+ return new CoreException(makeStatus(msg, t, IStatus.ERROR));
+ }
+
+ public static void log(String msg) {
+ DebugPlugin.log(makeStatus(msg, null, IStatus.INFO));
+ }
+
+ public static void error(String msg) {
+ DebugPlugin.log(makeStatus(msg, null, IStatus.ERROR));
+ }
+
+ public static void error(String msg, Throwable t) {
+ DebugPlugin.log(makeStatus(msg, t, IStatus.ERROR));
+ }
+
+ public static void warn(String msg) {
+ DebugPlugin.log(makeStatus(msg, null, IStatus.WARNING));
+ }
+
+ public static void warn(String msg, Throwable t) {
+ DebugPlugin.log(makeStatus(msg, t, IStatus.WARNING));
+ }
+
+ private static IStatus makeStatus(String msg, Throwable t, int status) {
+ if (t instanceof CoreException) {
+ CoreException c = (CoreException) t;
+ return c.getStatus();
+ } else {
+ return new Status(status, SigilCore.PLUGIN_ID, status, msg, t);
+ }
+ }
+
+ public static boolean isSigilProject(IProject resource) {
+ if ( resource == null ) return false;
+
+ if ( resource.isAccessible() && resource instanceof IProject ) {
+ IProject project = (IProject) resource;
+ try {
+ return project.hasNature(NATURE_ID);
+ } catch (CoreException e) {
+ error( e.getMessage(), e );
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ public static boolean hasProjectNature(IProject project)
+ throws CoreException {
+ return project.getNature(NATURE_ID) != null;
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ return ResourceBundle.getBundle("resources."
+ + SigilCore.class.getName(), Locale.getDefault(),
+ SigilCore.class.getClassLoader());
+ }
+
+ public static ISigilProjectModel create(IProject project)
+ throws CoreException {
+ if (project.hasNature(NATURE_ID)) {
+ return new SigilProject(project);
+ } else {
+ throw newCoreException("Project " + project.getName()
+ + " is not a sigil project", null);
+ }
+ }
+
+ /**
+ * The constructor
+ */
+ public SigilCore() {
+ plugin = this;
+ }
+
+ public void earlyStartup() {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+ * )
+ */
+ public void start(final BundleContext context) throws Exception {
+ super.start(context);
+
+ modelRoot = new SigilModelRoot();
+
+ repositoryConfig = new RepositoryConfiguration();
+
+ installs = new OSGiInstallManager();
+
+ globalRepositoryManager = new GlobalRepositoryManager();
+
+ registerModelElements(context);
+ registerResourceListeners();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+ * )
+ */
+ public void stop(BundleContext context) throws Exception {
+ if (descriptorTracker != null) {
+ descriptorTracker.close();
+ descriptorTracker = null;
+ }
+
+ if (registryTracker != null) {
+ registryTracker.close();
+ registryTracker = null;
+ }
+
+ if (serializerTracker != null) {
+ serializerTracker.close();
+ serializerTracker = null;
+ }
+
+ for ( SigilRepositoryManager m : repositoryManagers.values() ) {
+ m.destroy();
+ }
+
+ repositoryManagers.clear();
+
+ globalRepositoryManager.destroy();
+ globalRepositoryManager = null;
+
+ plugin = null;
+
+ super.stop(context);
+ }
+
+
+ public static boolean isBundledPath(String bp) throws CoreException {
+ boolean bundle = JavaHelper.isCachedBundle(bp);
+
+ if (!bundle) {
+ bundle = isProjectPath(bp);
+
+ if (!bundle) {
+ for (IBundleRepository r : getGlobalRepositoryManager().getRepositories()) {
+ bundle = isBundlePath(bp, r);
+ if (bundle)
+ break;
+ }
+ }
+ }
+
+ return bundle;
+ }
+
+ private static boolean isBundlePath(final String bp, IBundleRepository r) {
+ final AtomicBoolean flag = new AtomicBoolean();
+
+ IRepositoryVisitor visitor = new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle b) {
+ IPath path = b.getLocation();
+ if (path != null && path.toOSString().equals(bp)) {
+ flag.set(true);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ };
+
+ r.accept(visitor, ResolutionConfig.INDEXED_ONLY
+ | ResolutionConfig.LOCAL_ONLY);
+
+ return flag.get();
+ }
+
+ private static boolean isProjectPath(String bp) throws CoreException {
+ for (ISigilProjectModel p : SigilCore.getRoot().getProjects()) {
+ IPath path = p.findOutputLocation();
+
+ if (path.toOSString().equals(bp)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void registerResourceListeners() {
+ final IResourceChangeListener listener = new IResourceChangeListener() {
+ public void resourceChanged(IResourceChangeEvent event) {
+ IResourceDelta delta = event.getDelta();
+ if ( delta != null ) {
+ try {
+ delta.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ IResource resource = delta.getResource();
+ if(resource instanceof IProject) {
+ IProject project = (IProject) resource;
+ if ( SigilCore.isSigilProject(project) ) {
+ switch (delta.getKind()) {
+ case IResourceDelta.REMOVED:
+ case IResourceDelta.ADDED:
+ rebuildAllBundleDependencies(Job.getJobManager().createProgressGroup());
+ break;
+ }
+ }
+ // Recurse no more
+ return false;
+ }
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ error("Failed to update after change", e);
+ }
+ }
+ }
+ };
+
+ Job job = new Job("Initialising sigil resource listeners") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.POST_CHANGE);
+ return Status.OK_STATUS;
+ }
+ };
+ job.setSystem(true);
+ job.schedule();
+ }
+
+ private void registerModelElements(BundleContext context) {
+ // trick to get eclipse to lazy load BldCore for model elements
+ BldCore.getLicenseManager();
+ String uri = "http://sigil.codecauldron.org/xml/sigil-namespace";
+ ModelElementFactory.getInstance().register(ISigilProjectModel.class,
+ SigilProject.class, "project", "sigil", uri);
+ }
+
+ public static IOSGiInstallManager getInstallManager() {
+ return installs;
+ }
+
+ public static ISigilModelRoot getRoot() {
+ return modelRoot;
+ }
+
+ public static IRepositoryManager getGlobalRepositoryManager() {
+ return globalRepositoryManager;
+ }
+
+ public static IRepositoryManager getRepositoryManager(String set) {
+ SigilRepositoryManager manager = null;
+
+ if ( set == null ) {
+ manager = repositoryManagers.get( NULL );
+ if ( manager == null ) {
+ manager = new SigilRepositoryManager(null);
+ manager.initialise();
+ repositoryManagers.put( NULL, manager );
+ }
+ }
+ else {
+ manager = repositoryManagers.get(set);
+
+ if ( manager == null ) {
+ manager = new SigilRepositoryManager(set);
+ manager.initialise();
+ repositoryManagers.put( set, manager );
+ }
+ }
+
+ return manager;
+ }
+ public static IRepositoryManager getRepositoryManager(ISigilProjectModel model) {
+ return getRepositoryManager(loadProjectRepositorySet(model));
+ }
+
+ private static String loadProjectRepositorySet(ISigilProjectModel model) {
+ if ( model == null ) {
+ return null;
+ }
+
+ return model.getPreferences().get(REPOSITORY_SET, null );
+ }
+
+
+ public static IRepositoryConfiguration getRepositoryConfiguration() {
+ return repositoryConfig;
+ }
+
+ public static void rebuildAllBundleDependencies(IProgressMonitor monitor) {
+ Collection<ISigilProjectModel> projects = getRoot().getProjects();
+ if ( !projects.isEmpty() ) {
+ SubMonitor progress = SubMonitor.convert(monitor, projects.size()*20);
+ for ( ISigilProjectModel p : projects ) {
+ rebuild(p, progress);
+ }
+ }
+
+ monitor.done();
+ }
+
+ public static void rebuildBundleDependencies(ISigilProjectModel project, IProgressMonitor monitor) {
+ HashSet<ISigilProjectModel> affected = new HashSet<ISigilProjectModel>(project.findDependentProjects(monitor));
+ affected.add(project);
+
+ SubMonitor progress = SubMonitor.convert(monitor, affected.size()*20);
+ for (ISigilProjectModel dependent : affected) {
+ rebuild(dependent, progress);
+ }
+ }
+
+
+ private static void rebuild(ISigilProjectModel dependent, SubMonitor progress) {
+ try {
+ dependent.resetClasspath(progress.newChild(10));
+ dependent.getProject().build(IncrementalProjectBuilder.FULL_BUILD, progress.newChild(10));
+ } catch (CoreException e) {
+ SigilCore.error("Failed to rebuild " + dependent, e);
+ }
+ }
+
+ public IPath findDefaultBundleLocation(ISigilProjectModel m)
+ throws CoreException {
+ IPath loc = m.getProject().getLocation();
+ loc = loc.append(m.getJavaModel().getOutputLocation().removeFirstSegments(1));
+ loc = loc.removeLastSegments(1).append( "lib" );
+ return loc.append(m.getSymbolicName() + ".jar");
+ }
+
+ public static void makeSigilProject(IProject project,
+ IProgressMonitor monitor) throws CoreException {
+ IProjectDescription description = project.getDescription();
+
+ String[] natures = description.getNatureIds();
+ String[] newNatures = new String[natures.length + 1];
+ System.arraycopy(natures, 0, newNatures, 0, natures.length);
+ newNatures[natures.length] = SigilCore.NATURE_ID;
+ description.setNatureIds(newNatures);
+
+ ICommand sigilBuild = description.newCommand();
+ sigilBuild.setBuilderName(SigilCore.BUILDER_ID);
+
+ ICommand javaBuild = description.newCommand();
+ javaBuild.setBuilderName(JavaCore.BUILDER_ID);
+
+ description.setBuildSpec(new ICommand[] { javaBuild, sigilBuild });
+
+ project.setDescription(description, new SubProgressMonitor(monitor, 2));
+
+ IJavaProject java = JavaCore.create(project);
+ if (java.exists()) {
+ IClasspathEntry[] cp = java.getRawClasspath();
+ // XXX fix for http://jira.codecauldron.org/browse/SIGIL-304
+ if ( !isSigilOnClasspath(cp) ) {
+ ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(
+ Arrays.asList(cp));
+ entries.add(JavaCore.newContainerEntry(new Path(
+ SigilCore.CLASSPATH_CONTAINER_PATH)));
+ java.setRawClasspath(entries.toArray(new IClasspathEntry[entries
+ .size()]), monitor);
+ }
+ }
+ }
+
+ /**
+ * @param cp
+ * @return
+ */
+ private static boolean isSigilOnClasspath(IClasspathEntry[] cp) {
+ for ( IClasspathEntry e : cp ) {
+ if ( e.getEntryKind() == IClasspathEntry.CPE_CONTAINER && e.getPath().segment(0).equals(SigilCore.CLASSPATH_CONTAINER_PATH) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Image loadImage(URL url) throws IOException {
+ ImageRegistry registry = getDefault().getImageRegistry();
+
+ String key = url.toExternalForm();
+ Image img = registry.get( key );
+
+ if ( img == null ) {
+ img = openImage(url);
+ registry.put( key, img );
+ }
+
+ return img;
+ }
+
+ private static Image openImage(URL url) throws IOException {
+ Display display = Display.getCurrent();
+ if ( display == null ) {
+ display = Display.getDefault();
+ }
+
+ InputStream in = null;
+ try {
+ in = url.openStream();
+ return new Image(display, in);
+ }
+ finally {
+ if ( in != null ) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ error( "Failed to close stream", e );
+ }
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java
new file mode 100644
index 0000000..3e91ca1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstall.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Encapsulates all information about a particular OSGi install.
+ *
+ * @author dave
+ *
+ */
+public interface IOSGiInstall {
+
+ /**
+ * A unique id which can be used to refer to this install within the eclipse runtime.
+ * @return
+ */
+ String getId();
+
+ /**
+ * Where this install is located
+ * @return
+ */
+ IPath getInstallLocation();
+
+ /**
+ * @return
+ */
+ Map<String, String> getProperties();
+
+ /**
+ * @return
+ */
+ String[] getLaunchArguments();
+
+ /**
+ * @return
+ */
+ IPath getVarDirectory();
+
+ /**
+ * @return
+ */
+ IOSGiInstallType getType();
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java
new file mode 100644
index 0000000..ce066ea
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+
+public interface IOSGiInstallBuilder {
+ IOSGiInstall build(String id, IPath path) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java
new file mode 100644
index 0000000..c33f746
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallManager.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+public interface IOSGiInstallManager {
+ IOSGiInstall findInstall(String id);
+ String[] getInstallIDs();
+ IOSGiInstall[] getInstalls();
+ IOSGiInstall getDefaultInstall();
+ IOSGiInstallType findInstallType(String location);
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java
new file mode 100644
index 0000000..f975151
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/IOSGiInstallType.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.swt.graphics.Image;
+
+//import org.eclipse.swt.graphics.Image;
+
+public interface IOSGiInstallType {
+ /**
+ * @return
+ */
+ String getName();
+
+ /**
+ *
+ * @return
+ */
+ String getVersion();
+
+ /**
+ * @return
+ */
+ String getMainClass();
+
+ /**
+ * @return
+ */
+ String[] getClassPath();
+
+ /**
+ * @return
+ */
+ IPath getSourceLocation();
+
+ /**
+ * @return
+ */
+ IPath getJavaDocLocation();
+
+ /**
+ * Return the paths of any bundles that are started by default in this OSGi instance.
+ * @return
+ */
+ IPath[] getDefaultBundleLocations();
+
+ String getId();
+
+ Image getIcon();
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java
new file mode 100644
index 0000000..633f1f3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstall.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+
+public class OSGiInstall implements IOSGiInstall {
+
+ private String id;
+ private IPath installLocation;
+ private String[] launchArgs;
+ private Map<String, String> properties;
+ private IPath varDirectory;
+ private IOSGiInstallType type;
+
+ public IPath getVarDirectory() {
+ return varDirectory;
+ }
+
+ public void setVarDirectory(IPath varDirectory) {
+ this.varDirectory = varDirectory;
+ }
+
+ public OSGiInstall(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public IPath getInstallLocation() {
+ return installLocation;
+ }
+
+ public void setInstallLocation(IPath installLocation) {
+ this.installLocation = installLocation;
+ }
+
+ public String[] getLaunchArguments() {
+ return launchArgs;
+ }
+
+ public void setLaunchArguments(String[] launchArgs) {
+ this.launchArgs = launchArgs;
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String,String> properties) {
+ this.properties = properties;
+ }
+
+ public String toString() {
+ return "OSGiInstall[\n" +
+ "id=" + id + "\n" +
+ "type=" + type + "\n" +
+ "installLocation=" + installLocation + "\n" +
+ "launchArgs=" + Arrays.asList(launchArgs) + "\n" +
+ "properties=" + properties + "\n" +
+ "]";
+ }
+
+ public IOSGiInstallType getType() {
+ return type;
+ }
+
+ public void setType(IOSGiInstallType type) {
+ this.type = type;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java
new file mode 100644
index 0000000..e584f1c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/install/OSGiInstallType.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.install;
+
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.swt.graphics.Image;
+
+public class OSGiInstallType implements IOSGiInstallType {
+
+ private String id;
+ private String name;
+ private String version;
+ private String mainClass;
+ private String[] classPath;
+ private IPath javaDocLocation;
+ private IPath sourceLocation;
+ private IPath[] defaultBundleLocations;
+ private Image icon;
+
+ public Image getIcon() {
+ return icon;
+ }
+
+ public void setIcon(Image icon) {
+ this.icon = icon;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String[] getClassPath() {
+ return classPath;
+ }
+
+ public IPath[] getDefaultBundleLocations() {
+ return defaultBundleLocations;
+ }
+
+ public IPath getJavaDocLocation() {
+ return javaDocLocation;
+ }
+
+ public String getMainClass() {
+ return mainClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public IPath getSourceLocation() {
+ return sourceLocation;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public void setMainClass(String mainClass) {
+ this.mainClass = mainClass;
+ }
+
+ public void setClassPath(String[] classPath) {
+ this.classPath = classPath;
+ }
+
+ public void setJavaDocLocation(IPath javaDocLocation) {
+ this.javaDocLocation = javaDocLocation;
+ }
+
+ public void setSourceLocation(IPath sourceLocation) {
+ this.sourceLocation = sourceLocation;
+ }
+
+ public void setDefaultBundleLocations(IPath[] defaultBundleLocations) {
+ this.defaultBundleLocations = defaultBundleLocations;
+ }
+
+ public String toString() {
+ return "OSGiInstallType[\n" +
+ "name=" + name + "\n" +
+ "version=" + version + "\n" +
+ "mainClass=" + mainClass + "\n" +
+ "classPath=" + Arrays.asList(classPath) + "\n" +
+ "javaDocLocation=" + javaDocLocation + "\n" +
+ "sourceLocation=" + sourceLocation + "\n" +
+ "defaultBundleLocations=" + Arrays.asList(defaultBundleLocations) + "\n" +
+ "]";
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java
new file mode 100644
index 0000000..4b63ad3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/FileAdaptorFactory.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.adapter;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterFactory;
+
+/**
+ * @author savage
+ *
+ */
+public class FileAdaptorFactory implements IAdapterFactory {
+
+ public FileAdaptorFactory() {
+
+ }
+ private Class<?>[] types = new Class<?>[] { ISigilBundle.class };
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ public Object getAdapter( Object adaptableObject, Class adapterType ) {
+ Object adapted = null;
+
+ IFile file = (IFile) adaptableObject;
+
+ if ( ISigilBundle.class.equals( adapterType ) ) {
+ adapted = adaptBundle(file);
+ }
+
+ return adapted;
+ }
+
+ private Object adaptBundle(IFile file) {
+ Object adapted = null;
+ IProject project = file.getProject();
+ try {
+ if ( SigilCore.hasProjectNature( project ) ) {
+ ISigilProjectModel sigil = SigilCore.create( project );
+ ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+ bundle.setParent( sigil );
+ adapted = bundle;
+ }
+ }
+ catch ( CoreException e ) {
+ SigilCore.error( "Failed to construct bundle", e );
+ } catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to construct bundle", e );
+ }
+
+ return adapted;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+ */
+ @SuppressWarnings("unchecked")
+ public Class[] getAdapterList() {
+ return types;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java
new file mode 100644
index 0000000..4811476
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/adapter/ProjectAdaptorFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.adapter;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterFactory;
+
+/**
+ * @author savage
+ *
+ */
+public class ProjectAdaptorFactory implements IAdapterFactory {
+
+ private Class<?>[] types = new Class<?>[] { ISigilProjectModel.class };
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ public Object getAdapter( Object adaptableObject, Class adapterType ) {
+ Object adapted = null;
+
+ IProject project = (IProject) adaptableObject;
+
+ if ( ISigilProjectModel.class.equals( adapterType ) ) {
+ adapted = adaptProject(project);
+ }
+
+ return adapted;
+ }
+
+ private Object adaptProject(IProject project) {
+ try {
+ if ( SigilCore.isSigilProject(project) ) {
+ return SigilCore.create(project);
+ }
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
+ */
+ @SuppressWarnings("unchecked")
+ public Class[] getAdapterList() {
+ return types;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java
new file mode 100644
index 0000000..5ac8117
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/BuildConsole.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.builders;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class BuildConsole extends MessageConsole {
+
+ private static final ImageDescriptor imageDescriptor = null;
+ private MessageConsoleStream stream;
+
+ public BuildConsole() {
+ super("Sigil Build", imageDescriptor, true);
+ }
+
+ public synchronized MessageConsoleStream getMessageStream() {
+ if ( stream == null ) {
+ stream = newMessageStream();
+ }
+ return stream;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java
new file mode 100644
index 0000000..acea59a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/builders/SigilIncrementalProjectBuilder.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.builders;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.cauldron.bld.bnd.BundleBuilder;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaModelMarker;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class SigilIncrementalProjectBuilder extends IncrementalProjectBuilder {
+
+ @Override
+ protected IProject[] build(int kind, @SuppressWarnings("unchecked")Map args, IProgressMonitor monitor)
+ throws CoreException {
+ IProject project = getProject();
+
+ if ( checkOk( project ) ) {
+ switch ( kind ) {
+ case CLEAN_BUILD:
+ case FULL_BUILD:
+ fullBuild( project, monitor );
+ break;
+ case AUTO_BUILD:
+ case INCREMENTAL_BUILD:
+ autoBuild( project, monitor );
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param install
+ * @param project
+ * @param monitor
+ * @throws CoreException
+ */
+ private void autoBuild(IProject project,
+ IProgressMonitor monitor) throws CoreException {
+ IResourceDelta delta = getDelta(project);
+ final boolean[] changed = new boolean[1];
+ ISigilProjectModel sigil = SigilCore.create(project);
+ final IPath bldRoot = sigil.findBundleLocation().removeLastSegments(1);
+
+ delta.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ if ( !changed[0] ) {
+ IResource res = delta.getResource();
+ if ( res.getType() == IResource.FILE ) {
+ changed[0] = !bldRoot.isPrefixOf(res.getLocation());
+ }
+ }
+ return !changed[0];
+ }
+ });
+
+ if ( changed[0] ) {
+ doBuild(project, monitor);
+ }
+ }
+
+ /**
+ * @param install
+ * @param project
+ * @param monitor
+ * @throws CoreException
+ */
+ private void fullBuild(IProject project,
+ IProgressMonitor monitor) throws CoreException {
+ doBuild(project, monitor);
+ }
+
+ private boolean checkOk(IProject project) throws CoreException {
+ IMarker[] markers = project.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
+
+ for ( IMarker m : markers ) {
+ Integer s = (Integer) m.getAttribute(IMarker.SEVERITY);
+ if ( s != null && s.equals( IMarker.SEVERITY_ERROR ) ) {
+ SigilCore.log( "Skipping " + project.getName() + " build due to unresolved errors" );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void doBuild(IProject project, IProgressMonitor monitor ) throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project);
+ IBldProject bld = sigil.getBldProject();
+
+ File[] classpath = buildClasspath(sigil, monitor);
+
+ String destPattern = buildDestPattern(sigil);
+
+ Properties env = new Properties();
+
+ BundleBuilder bb = new BundleBuilder(bld, classpath, destPattern, env);
+
+ for (IBldProject.IBldBundle bundle : bld.getBundles()) {
+ String id = bundle.getId();
+ loginfo("creating bundle: " + id);
+ int nWarn = 0;
+ int nErr = 0;
+ String msg = "";
+
+ try {
+ boolean modified = bb.createBundle(bundle, false, new BundleBuilder.Log() {
+ public void warn(String msg) {
+ logwarn(msg);
+ }
+ public void verbose(String msg) {
+ loginfo(msg);
+ }
+ });
+ nWarn = bb.warnings().size();
+ if (!modified) {
+ msg = " (not modified)";
+ }
+ } catch (Exception e) {
+ List<String> errors = bb.errors();
+ if (errors != null) {
+ nErr = errors.size();
+ for (String err : errors) {
+ logerror(err);
+ }
+ }
+ throw SigilCore.newCoreException("Failed to create: " + id + ": " + e, e);
+ } finally {
+ loginfo(id + ": " + count(nErr, "error") + ", " + count(nWarn, "warning") + msg);
+ }
+ }
+ }
+
+ private static void loginfo(String message) {
+ BuildConsole console = findConsole();
+ MessageConsoleStream stream = console.getMessageStream();
+ stream.println("INFO: " + message);
+ }
+
+ private static void logwarn(String message) {
+ BuildConsole console = findConsole();
+ MessageConsoleStream stream = console.getMessageStream();
+ stream.println("WARN: " + message);
+ }
+
+ private static void logerror(String message) {
+ BuildConsole console = findConsole();
+ MessageConsoleStream stream = console.getMessageStream();
+ stream.println("ERROR: " + message);
+ }
+
+ private static BuildConsole findConsole() {
+ BuildConsole console = null;
+
+ IConsoleManager manager = ConsolePlugin.getDefault().getConsoleManager();
+
+ for ( IConsole c : manager.getConsoles() ) {
+ if ( c instanceof BuildConsole ) {
+ console = (BuildConsole) c;
+ break;
+ }
+ }
+
+ if ( console == null ) {
+ console = new BuildConsole();
+ manager.addConsoles( new IConsole[] { console } );
+ }
+
+ return console;
+ }
+
+ private String buildDestPattern(ISigilProjectModel sigil) throws CoreException {
+ IPath loc = sigil.findBundleLocation().removeLastSegments(1);
+
+ loc.toFile().mkdirs();
+
+ return loc.toOSString() + File.separator + "[name].jar";
+ }
+
+ private File[] buildClasspath(ISigilProjectModel sigil, IProgressMonitor monitor) throws CoreException {
+ ArrayList<File> files = new ArrayList<File>();
+
+ buildLocalClasspath(sigil, files);
+ buildExternalClasspath(sigil, files, monitor);
+
+ return files.toArray( new File[files.size()] );
+ }
+
+ private void buildExternalClasspath(ISigilProjectModel sigil,
+ ArrayList<File> files, IProgressMonitor monitor) throws CoreException {
+ Collection<IClasspathEntry> entries = sigil.findExternalClasspath(monitor);
+ files.ensureCapacity(files.size() + entries.size());
+
+ for ( IClasspathEntry cp : entries ) {
+ convert(cp, sigil, files);
+ }
+ }
+
+ private void buildLocalClasspath(ISigilProjectModel sigil, ArrayList<File> files) throws CoreException {
+ Collection<IClasspathEntry> entries = JavaHelper.findClasspathEntries(sigil.getBundle());
+ files.ensureCapacity(files.size() + entries.size());
+ for ( IClasspathEntry cp : entries ) {
+ convert(cp, sigil, files);
+ }
+
+ if ( !sigil.getBundle().getComposites().isEmpty() ) {
+ throw new IllegalStateException("XXX-FIXME-XXX");
+ }
+ }
+
+ private void convert(IClasspathEntry cp, ISigilProjectModel sigil, ArrayList<File> files) throws CoreException {
+ switch( cp.getEntryKind() ) {
+ case IClasspathEntry.CPE_PROJECT: {
+ IProject p = findProject(cp.getPath());
+ ISigilProjectModel project = SigilCore.create(p);
+ for ( String scp : project.getBundle().getClasspathEntrys() ) {
+ IClasspathEntry jcp = project.getJavaModel().decodeClasspathEntry(scp);
+ convert( jcp, project, files );
+ }
+ break;
+ }
+ case IClasspathEntry.CPE_SOURCE : {
+ IPath path = cp.getOutputLocation() == null ? sigil.getJavaModel().getOutputLocation() : cp.getOutputLocation();
+ IFolder buildFolder = sigil.getProject().getFolder(path.removeFirstSegments(1));
+ if ( buildFolder.exists() ) {
+ files.add(buildFolder.getLocation().toFile());
+ }
+ break;
+ }
+ case IClasspathEntry.CPE_LIBRARY: {
+ IPath p = cp.getPath();
+
+ IPath ppath = sigil.getProject().getFullPath();
+
+ if ( ppath.isPrefixOf(p) ) {
+ p = sigil.getProject().getLocation().append( p.removeFirstSegments(1) );
+ }
+
+ files.add( p.toFile() );
+ break;
+ }
+ case IClasspathEntry.CPE_VARIABLE:
+ cp = JavaCore.getResolvedClasspathEntry(cp);
+ if ( cp != null ) {
+ IPath p = cp.getPath();
+ files.add( p.toFile() );
+ }
+ break;
+ }
+ }
+
+ private IProject findProject(IPath path) throws CoreException {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ for ( IProject p : root.getProjects() ) {
+ IPath projectPath = p.getFullPath();
+ if ( projectPath.equals( path ) ) {
+ return p;
+ }
+ }
+
+ throw SigilCore.newCoreException("No such project " + path, null);
+ }
+
+ private String count(int count, String msg) {
+ return count + " " + msg + (count == 1 ? "" : "s");
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java
new file mode 100644
index 0000000..ce5f20f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/install/OSGiInstallManager.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.install;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.install.IOSGiInstallBuilder;
+import org.cauldron.sigil.install.IOSGiInstallManager;
+import org.cauldron.sigil.install.IOSGiInstallType;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+
+public class OSGiInstallManager implements IOSGiInstallManager, IPropertyChangeListener {
+ private static final int NORMAL_PRIORITY = 0;
+
+ private LinkedList<IOSGiInstallBuilder> builders = new LinkedList<IOSGiInstallBuilder>();
+
+ private HashMap<IPath, IOSGiInstall> pathToinstall = new HashMap<IPath, IOSGiInstall>();
+ private HashMap<String, IOSGiInstall> idToInstall = new HashMap<String, IOSGiInstall>();
+
+ private String defaultId;
+
+ private boolean initialised;
+
+ public IOSGiInstall findInstall(String id) {
+ init();
+ return idToInstall.get(id);
+ }
+
+ public String[] getInstallIDs() {
+ init();
+ return idToInstall.keySet().toArray( new String[idToInstall.size()] );
+ }
+
+ public IOSGiInstall[] getInstalls() {
+ init();
+ return idToInstall.values().toArray( new IOSGiInstall[idToInstall.size()] );
+ }
+
+ public IOSGiInstall getDefaultInstall() {
+ init();
+ return findInstall(defaultId);
+ }
+
+ public IOSGiInstallType findInstallType(String location) {
+ IOSGiInstallType type = null;
+
+ try {
+ IOSGiInstall install = buildInstall("tmp", new Path( location ) );
+ type = install == null ? null : install.getType();
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to build install", e);
+ }
+
+ return type;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ synchronized( this ) {
+ if ( event.getProperty().equals(SigilCore.OSGI_INSTALLS) ) {
+ clearInstalls();
+ String val = (String) event.getNewValue();
+ addInstalls(val);
+ }
+ else if ( event.getProperty().equals( SigilCore.OSGI_DEFAULT_INSTALL_ID ) ) {
+ defaultId = (String) event.getNewValue();
+ }
+ }
+ }
+
+ private void init() {
+ boolean show = false;
+
+ IPreferenceStore prefs = getPreferenceStore();
+
+ synchronized( this ) {
+ if ( !initialised ) {
+ initialised = true;
+
+ prefs.addPropertyChangeListener(this);
+
+ String val = prefs.getString(SigilCore.OSGI_INSTALLS);
+
+ boolean noAsk = prefs.getBoolean(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL);
+ if(val == null || val.trim().length() == 0) {
+ show = !noAsk;
+ }
+ else {
+ addInstalls(val);
+ defaultId = prefs.getString(SigilCore.OSGI_DEFAULT_INSTALL_ID);
+ }
+ }
+ }
+
+ if ( show ) {
+ showInstallPrefs(prefs);
+ }
+ }
+
+ private void addInstalls(String prop) {
+ if ( prop != null && prop.trim().length() > 0 ) {
+ IPreferenceStore prefs = getPreferenceStore();
+
+ for (String id : prop.split(",")) {
+ String path = prefs.getString( SigilCore.OSGI_INSTALL_PREFIX + id );
+ addInstall(id, new Path( path ) );
+ }
+ }
+ }
+
+ private IPreferenceStore getPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ private void showInstallPrefs(final IPreferenceStore prefs) {
+ Runnable r = new Runnable() {
+ public void run() {
+ MessageDialogWithToggle questionDialog = MessageDialogWithToggle.openYesNoQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Sigil Configuration", "Missing OSGi installation. Open preferences to configure it now?", "Do not show this message again", false, null, null);
+ prefs.setValue(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL, questionDialog.getToggleState());
+ if(questionDialog.getReturnCode() == IDialogConstants.YES_ID) {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, SigilCore.OSGI_INSTALLS_PREFERENCES_ID, null, null);
+ dialog.open();
+ }
+ }
+ };
+ Display d = Display.getCurrent();
+ if ( d == null ) {
+ d = Display.getDefault();
+ d.asyncExec(r);
+ }
+ else {
+ d.syncExec(r);
+ }
+ }
+
+ private IOSGiInstall addInstall(String id, IPath path) {
+ IOSGiInstall install = pathToinstall.get(path);
+
+ if ( install == null ) {
+ try {
+ install = buildInstall(id, path);
+ if ( install != null ) {
+ pathToinstall.put( path, install );
+ idToInstall.put( install.getId(), install );
+ }
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to build install for " + path, e);
+ }
+ }
+
+ return install;
+ }
+
+ private IOSGiInstall buildInstall(String id, IPath path) throws CoreException {
+ initBuilders();
+ IOSGiInstall install = null;
+
+ for ( IOSGiInstallBuilder b : builders ) {
+ install = b.build(id, path);
+
+ if ( install != null ) {
+ break;
+ }
+ }
+
+ return install;
+ }
+
+ private void clearInstalls() {
+ idToInstall.clear();
+ pathToinstall.clear();
+ }
+
+ private void initBuilders() {
+ synchronized( builders ) {
+ if ( builders.isEmpty() ) {
+ final HashMap<IOSGiInstallBuilder, Integer> tmp = new HashMap<IOSGiInstallBuilder, Integer>();
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint p = registry.getExtensionPoint(SigilCore.INSTALL_BUILDER_EXTENSION_POINT_ID);
+ for ( IExtension e : p.getExtensions() ) {
+ for ( IConfigurationElement c : e.getConfigurationElements() ) {
+ createBuilderFromElement(c, tmp);
+ }
+ }
+
+ builders = new LinkedList<IOSGiInstallBuilder>(tmp.keySet());
+ Collections.sort(builders, new Comparator<IOSGiInstallBuilder>() {
+ public int compare(IOSGiInstallBuilder o1, IOSGiInstallBuilder o2) {
+ int p1 = tmp.get(o1);
+ int p2 = tmp.get(o2);
+
+ if ( p1 == p2 ) {
+ return 0;
+ }
+ else if ( p1 > p2 ) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private void createBuilderFromElement(IConfigurationElement c, Map<IOSGiInstallBuilder, Integer> builder) {
+ try {
+ IOSGiInstallBuilder b = (IOSGiInstallBuilder) c.createExecutableExtension("class");
+ int priority = parsePriority( c );
+ builder.put(b, priority);
+ } catch (CoreException e) {
+ SigilCore.error("Failed to create builder", e);
+ }
+ }
+
+ private int parsePriority(IConfigurationElement c) {
+ String str = c.getAttribute("priority");
+
+ if ( str == null ) {
+ return NORMAL_PRIORITY;
+ }
+ else {
+ return Integer.parseInt(str);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java
new file mode 100644
index 0000000..ea05121
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilModelRoot.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.project;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.eclipse.ILibraryImport;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilModelRoot;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class SigilModelRoot implements ISigilModelRoot {
+ public List<ISigilProjectModel> getProjects() {
+ IProject[] all = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ ArrayList<ISigilProjectModel> projects = new ArrayList<ISigilProjectModel>(all.length);
+ for (IProject p : all) {
+ try {
+ if (p.isOpen() && p.hasNature(SigilCore.NATURE_ID)) {
+ ISigilProjectModel n = SigilCore.create(p);
+ projects.add(n);
+ }
+ } catch (CoreException e) {
+ SigilCore.error("Failed to build model element", e);
+ }
+ }
+
+ return projects;
+ }
+
+ public Collection<ISigilProjectModel> resolveDependentProjects(
+ ISigilProjectModel sigil, IProgressMonitor monitor) {
+ HashSet<ISigilProjectModel> dependents = new HashSet<ISigilProjectModel>();
+
+ for (ISigilProjectModel n : getProjects()) {
+ if (!sigil.equals(n)) {
+ for (IPackageExport pe : sigil.getBundle().getBundleInfo().getExports()) {
+ for (IPackageImport i : n.getBundle().getBundleInfo()
+ .getImports()) {
+ if (pe.getPackageName().equals(i.getPackageName())
+ && i.getVersions().contains(pe.getVersion())) {
+ dependents.add(n);
+ }
+ }
+
+ for (ILibraryImport l : n.getBundle().getBundleInfo().getLibraryImports()) {
+ ILibrary lib = SigilCore.getRepositoryManager(sigil).resolveLibrary(l);
+
+ if (lib != null) {
+ for (IPackageImport i : lib.getImports()) {
+ if (pe.getPackageName().equals(
+ i.getPackageName())
+ && i.getVersions().contains(
+ pe.getVersion())) {
+ dependents.add(n);
+ }
+ }
+ } else {
+ SigilCore.error("No library found for " + l);
+ }
+ }
+ }
+
+ for (IRequiredBundle r : n.getBundle().getBundleInfo().getRequiredBundles()) {
+ if (sigil.getSymbolicName().equals(r.getSymbolicName())
+ && r.getVersions().contains(sigil.getVersion())) {
+ dependents.add(n);
+ }
+ }
+ }
+ }
+
+ return dependents;
+ }
+
+ public Collection<ISigilBundle> resolveBundles(ISigilProjectModel sigil, IModelElement element, boolean includeOptional, IProgressMonitor monitor) throws CoreException {
+ int options = ResolutionConfig.INCLUDE_DEPENDENTS;
+ if ( includeOptional ) {
+ options |= ResolutionConfig.INCLUDE_OPTIONAL;
+ }
+
+ ResolutionConfig config = new ResolutionConfig(options);
+ try {
+ IBundleResolver resolver = SigilCore.getRepositoryManager(sigil).getBundleResolver();
+ IResolution resolution = resolver.resolve(element, config, new ResolutionMonitorAdapter(monitor));
+ resolution.synchronize(monitor);
+ return resolution.getBundles();
+ } catch (ResolutionException e) {
+ throw SigilCore.newCoreException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java
new file mode 100644
index 0000000..7a7f3af
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/project/SigilProject.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.project;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.cauldron.bld.config.BldFactory;
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ThreadProgressMonitor;
+import org.cauldron.sigil.model.AbstractCompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IParent;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.osgi.framework.Version;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProject extends AbstractCompoundModelElement implements ISigilProjectModel {
+
+ private static final long serialVersionUID = 1L;
+
+ private IFile bldProjectFile;
+ private IProject project;
+ private IBldProject bldProject;
+
+ private ISigilBundle bundle;
+
+ private IEclipsePreferences preferences;
+
+ public SigilProject() {
+ super( "Sigil Project" );
+ }
+
+ public SigilProject(IProject project) throws CoreException {
+ this();
+ this.project = project;
+ bldProjectFile = project.getFile( new Path( SigilCore.SIGIL_PROJECT_FILE) );
+ }
+
+ // to aid testing conversion between project file formats
+ public InputStream saveBundle(ISigilBundle b) throws CoreException {
+ setBundle(b);
+ // FIXME causes NPE in JavaHelper
+ // calculateUses();
+ return buildContents();
+ }
+
+ public void save(IProgressMonitor monitor) throws CoreException {
+ SubMonitor progress = SubMonitor.convert(monitor, 100);
+
+ calculateUses();
+
+ bldProjectFile.setContents( buildContents(), IFile.KEEP_HISTORY, progress.newChild(10));
+
+ IRepositoryManager manager = SigilCore.getRepositoryManager(this);
+ ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL);
+
+ try {
+ IResolution res = manager.getBundleResolver().resolve(this, config, new ResolutionMonitorAdapter(progress.newChild(10)));
+ if ( !res.isSynchronized() ) {
+ res.synchronize(progress.newChild(60));
+ }
+ } catch (ResolutionException e) {
+ throw SigilCore.newCoreException("Failed to synchronize dependencies", e);
+ }
+
+
+ progress.setWorkRemaining(40);
+
+ SigilCore.rebuildBundleDependencies( this, progress.newChild(20) );
+ }
+
+ /**
+ * Returns the project custom preference pool.
+ * Project preferences may include custom encoding.
+ * @return IEclipsePreferences or <code>null</code> if the project
+ * does not have a java nature.
+ */
+ public Preferences getPreferences(){
+ synchronized(this) {
+ if ( preferences == null ) {
+ preferences = loadPreferences();
+ }
+
+ return preferences;
+ }
+ }
+
+
+ /**
+ * @return
+ */
+ private synchronized IEclipsePreferences loadPreferences() {
+ IScopeContext context = new ProjectScope(getProject());
+ final IEclipsePreferences eclipsePreferences = context.getNode(SigilCore.PLUGIN_ID);
+
+ // Listen to node removal from parent in order to reset cache
+ INodeChangeListener nodeListener = new IEclipsePreferences.INodeChangeListener() {
+ public void added(IEclipsePreferences.NodeChangeEvent event) {
+ // do nothing
+ }
+
+ public void removed(IEclipsePreferences.NodeChangeEvent event) {
+ if (event.getChild() == eclipsePreferences) {
+ synchronized( SigilProject.this ) {
+ preferences = null;
+ }
+ ((IEclipsePreferences) eclipsePreferences.parent()).removeNodeChangeListener(this);
+ }
+ }
+ };
+
+ ((IEclipsePreferences) eclipsePreferences.parent()).addNodeChangeListener(nodeListener);
+
+ return eclipsePreferences;
+ }
+
+ public Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor) throws CoreException {
+ return JavaHelper.resolveClasspathEntrys(this, monitor);
+ }
+
+ private void calculateUses() {
+ visit( new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) element;
+ try {
+ pe.setUses( Arrays.asList( JavaHelper.findUses(pe.getPackageName(), SigilProject.this ) ) );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to build uses list for " + pe, e );
+ }
+ }
+ return true;
+ }
+ } );
+ }
+
+ public Collection<ISigilProjectModel> findDependentProjects(IProgressMonitor monitor) {
+ return SigilCore.getRoot().resolveDependentProjects(this, monitor);
+ }
+
+ public Version getVersion() {
+ ISigilBundle bundle = getBundle();
+ return bundle == null ? null : bundle.getBundleInfo() == null ? null : bundle.getBundleInfo().getVersion();
+ }
+
+ public String getSymbolicName() {
+ ISigilBundle bundle = getBundle();
+ return bundle == null ? null : bundle.getBundleInfo() == null ? null : bundle.getBundleInfo().getSymbolicName();
+ }
+
+ public IProject getProject() {
+ return project;
+ }
+
+ public ISigilBundle getBundle() {
+ if ( bundle == null && bldProjectFile != null ) {
+ synchronized( bldProjectFile ) {
+ try {
+ if ( bldProjectFile.getLocation().toFile().exists() ) {
+ bundle = parseContents(bldProjectFile);
+ }
+ else {
+ bundle = setupDefaults();
+ NullProgressMonitor npm = new NullProgressMonitor();
+ bldProjectFile.create( buildContents(), true /* force */, npm);
+ project.refreshLocal( IResource.DEPTH_INFINITE, npm );
+ }
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to build bundle", e);
+ }
+ }
+ }
+ return bundle;
+ }
+
+ public void setBundle(ISigilBundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public IJavaProject getJavaModel() {
+ return JavaCore.create( project );
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( obj == null ) return false;
+
+ if ( obj == this ) return true;
+
+ try {
+ SigilProject p = (SigilProject) obj;
+ return getSymbolicName().equals( p.getSymbolicName() ) && (getVersion() == null ? p.getVersion() == null : getVersion().equals( p.getVersion() ));
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ // TODO Auto-generated method stub
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "SigilProject[" + getSymbolicName() + ":" + getVersion() + "]";
+ }
+
+ public void resetClasspath(IProgressMonitor monitor) throws CoreException {
+ Path containerPath = new Path( SigilCore.CLASSPATH_CONTAINER_PATH );
+ IJavaProject java = getJavaModel();
+ ClasspathContainerInitializer init = JavaCore.getClasspathContainerInitializer(SigilCore.CLASSPATH_CONTAINER_PATH);
+ ThreadProgressMonitor.setProgressMonitor(monitor);
+ try {
+ init.requestClasspathContainerUpdate(containerPath, java, null);
+ }
+ finally {
+ ThreadProgressMonitor.setProgressMonitor(null);
+ }
+ }
+
+ public IPath findBundleLocation() throws CoreException {
+ IPath p = getBundle().getLocation();
+ if ( p == null ) {
+ p = SigilCore.getDefault().findDefaultBundleLocation(this);
+ }
+ return p;
+ }
+
+ public IModelElement findImport(final String packageName, final IProgressMonitor monitor) {
+ final IModelElement[] found = new IModelElement[1];
+
+ visit( new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) element;
+ if ( pi.getPackageName().equals( packageName ) ) {
+ found[0] = pi;
+ return false;
+ }
+ }
+ else if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) element;
+ try {
+ IRepositoryManager manager = SigilCore.getRepositoryManager(SigilProject.this);
+ ResolutionConfig config = new ResolutionConfig(ResolutionConfig.IGNORE_ERRORS);
+ IResolution res = manager.getBundleResolver().resolve(rb, config, new ResolutionMonitorAdapter(monitor));
+ ISigilBundle b = res.getProvider(rb);
+ for ( IPackageExport pe : b.getBundleInfo().getExports() ) {
+ if ( pe.getPackageName().equals( packageName ) ) {
+ found[0] = rb;
+ return false;
+ }
+ }
+ } catch (ResolutionException e) {
+ SigilCore.error( "Failed to resolve " + rb, e );
+ }
+ }
+ return true;
+ }
+
+ });
+
+ return found[0];
+ }
+
+ public boolean isInClasspath(String packageName, IProgressMonitor monitor) throws CoreException {
+ if ( findImport(packageName, monitor) != null ) {
+ return true;
+ }
+
+ for ( String path : getBundle().getClasspathEntrys() ) {
+ IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
+ for ( IPackageFragmentRoot root : getJavaModel().findPackageFragmentRoots(cp) ) {
+ if ( findPackage( packageName, root ) ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isInClasspath(ISigilBundle bundle) {
+ for ( String path : getBundle().getClasspathEntrys() ) {
+ IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path);
+ switch ( cp.getEntryKind() ) {
+ case IClasspathEntry.CPE_PROJECT:
+ ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class);
+ return p != null && cp.getPath().equals(p.getProject().getFullPath());
+ case IClasspathEntry.CPE_LIBRARY:
+ return cp.getPath().equals(bundle.getLocation());
+ }
+ }
+
+ return false;
+ }
+
+ private boolean findPackage(String packageName, IParent parent) throws JavaModelException {
+ for ( IJavaElement e : parent.getChildren() ) {
+ if ( e.getElementType() == IJavaElement.PACKAGE_FRAGMENT ) {
+ return e.getElementName().equals( packageName );
+ }
+
+ if ( e instanceof IParent ) {
+ if ( findPackage(packageName, (IParent) e) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private ISigilBundle setupDefaults() {
+ ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class);
+ IBundleModelElement info = ModelElementFactory.getInstance().newModelElement( IBundleModelElement.class );
+ info.setSymbolicName(project.getName());
+ bundle.setBundleInfo(info);
+ bundle.setParent(this);
+ return bundle;
+ }
+
+
+ private ISigilBundle parseContents(IFile projectFile) throws CoreException {
+ /*if ( !projectFile.isSynchronized(IResource.DEPTH_ONE) ) {
+ projectFile.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
+ }*/
+
+ if ( projectFile.getName().equals( SigilCore.SIGIL_PROJECT_FILE) ) {
+ return parseBldContents(projectFile.getLocationURI());
+ }
+ else {
+ throw SigilCore.newCoreException("Unexpected project file: " + projectFile.getName(), null );
+ }
+ }
+
+ private ISigilBundle parseBldContents(URI uri) throws CoreException {
+ try {
+ bldProject = BldFactory.getProject(uri, true);
+ ISigilBundle bundle = bldProject.getDefaultBundle();
+ bundle.setParent(this);
+ return bundle;
+ } catch (IOException e) {
+ throw SigilCore.newCoreException( "Failed to parse " + uri, e);
+ }
+ }
+
+ private InputStream buildContents() throws CoreException {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ try {
+ if (bldProject == null) {
+ bldProject = BldFactory.newProject(bldProjectFile.getLocationURI(), null);
+ }
+ bldProject.setDefaultBundle(getBundle());
+ bldProject.saveTo(buf);
+ } catch (IOException e) {
+ throw SigilCore.newCoreException("Failed to save project file", e);
+ }
+ return new ByteArrayInputStream(buf.toByteArray());
+ }
+
+// private InputStream buildXMLContents() throws CoreException {
+// Serializer serializer = SigilCore.getDefault().getDescriptorSerializer();
+//
+// ByteArrayOutputStream buf = new ByteArrayOutputStream();
+//
+// try {
+// serializer.serialize(getBundle(), buf);
+// } catch (SerializingException e) {
+// throw SigilCore.newCoreException("Failed to serialize " + this, e);
+// }
+//
+// return new ByteArrayInputStream(buf.toByteArray());
+// }
+
+ public String getName() {
+ return getProject().getName();
+ }
+
+ public IPath findOutputLocation() throws CoreException {
+ return getProject().getLocation().append(
+ getJavaModel().getOutputLocation()
+ .removeFirstSegments(1));
+ }
+
+ public IBldProject getBldProject() throws CoreException {
+ try {
+ return BldFactory.getProject(project.getFile(IBldProject.PROJECT_FILE).getLocationURI());
+ } catch (IOException e) {
+ throw SigilCore.newCoreException("Failed to get project file: ",e);
+ }
+ }
+
+ public boolean isInBundleClasspath(IPackageFragmentRoot root) throws JavaModelException {
+ String enc = getJavaModel().encodeClasspathEntry(root.getRawClasspathEntry());
+ return getBundle().getClasspathEntrys().contains( enc.trim() );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java
new file mode 100644
index 0000000..592e75b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryConfiguration.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Bundle;
+
+public class RepositoryConfiguration implements IRepositoryConfiguration {
+
+ private static final String REPOSITORY = "repository.";
+ private static final String REPOSITORY_SET = REPOSITORY + "set.";
+ private static final String REPOSITORY_SETS = REPOSITORY + "sets";
+ private static final String REPOSITORY_TIMESTAMP = REPOSITORY + "timestamp";
+ private static final String INSTANCES = ".instances";
+ private static final String NAME = ".name";
+ private static final String LOC = ".loc";
+ private static final String TIMESTAMP = ".timestamp";
+
+ public static final String REPOSITORY_DEFAULT_SET = REPOSITORY + "default.set";
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.repository.management.IRepositoryManagement#loadRepositories()
+ */
+ public List<IRepositoryModel> loadRepositories() {
+ IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+
+ ArrayList<IRepositoryModel> repositories = new ArrayList<IRepositoryModel>();
+
+ for ( RepositoryType type : loadRepositoryTypes() ) {
+ String typeID = type.getId();
+
+ if ( type.isDynamic() ) {
+ String instances = prefs.getString( REPOSITORY + typeID + INSTANCES );
+ if ( instances.trim().length() > 0 ) {
+ for( String instance : instances.split(",") ) {
+ String key = REPOSITORY + typeID + "." + instance;
+ repositories.add( loadRepository(instance, key, type, prefs) );
+ }
+ }
+ }
+ else {
+ String key = REPOSITORY + typeID;
+ repositories.add( loadRepository(typeID, key, type, prefs) );
+ }
+
+ }
+
+ return repositories;
+ }
+
+ public IRepositoryModel findRepository(String id) {
+ for (IRepositoryModel model : loadRepositories()) {
+ if ( model.getId().equals( id ) ) {
+ return model;
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.repository.management.IRepositoryManagement#saveRepositories(java.util.List)
+ */
+ public void saveRepositories(List<IRepositoryModel> repositories) throws CoreException {
+ IPreferenceStore prefs = getPreferences();
+
+ HashMap<IRepositoryType, List<IRepositoryModel>> mapped = new HashMap<IRepositoryType, List<IRepositoryModel>>(repositories.size());
+
+ saveRepositoryPreferences(repositories, mapped);
+ createNewEntries(mapped, prefs);
+ deleteOldEntries(repositories,prefs);
+ // time stamp is used as a signal to the manager
+ // to update its view of the stored repositories
+ timeStamp(prefs);
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.repository.management.IRepositoryManagement#loadRepositoryTypes()
+ */
+ public List<RepositoryType> loadRepositoryTypes() {
+ List<RepositoryType> repositories = new ArrayList<RepositoryType>();
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+
+ IExtensionPoint p = registry.getExtensionPoint(SigilCore.REPOSITORY_PROVIDER_EXTENSION_POINT_ID);
+
+ for ( IExtension e : p.getExtensions() ) {
+ for ( IConfigurationElement c : e.getConfigurationElements() ) {
+ String id = c.getAttribute("id");
+ String type = c.getAttribute("type");
+ boolean dynamic = Boolean.valueOf( c.getAttribute("dynamic") );
+ String icon = c.getAttribute("icon");
+ Image image = (icon == null || icon.trim().length() == 0) ? null : loadImage(e, icon);
+ repositories.add( new RepositoryType(id, type, dynamic, image ) );
+ }
+ }
+
+ return repositories;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.repository.management.IRepositoryManagement#newRepositoryElement(org.cauldron.sigil.repository.management.RepositoryType)
+ */
+ public IRepositoryModel newRepositoryElement(IRepositoryType type) {
+ String id = UUID.randomUUID().toString();
+ PreferenceStore prefs = new PreferenceStore();
+ RepositoryModel element = new RepositoryModel(id, "", type, prefs);
+ prefs.setFilename(makeFileName(element));
+ prefs.setValue("id", id);
+ return element;
+ }
+
+ public IRepositorySet getDefaultRepositorySet() {
+ //int level = findLevel( key + LEVEL, type, prefs );
+ ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+ for ( String s : PrefsUtils.stringToArray(getPreferences().getString( REPOSITORY_DEFAULT_SET)) ) {
+ reps.add( findRepository(s) );
+ }
+ return new RepositorySet( reps );
+ }
+
+ public IRepositorySet getRepositorySet(String name) {
+ String key = REPOSITORY_SET + name;
+ if ( getPreferences().contains(key) ) {
+ ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+ for ( String s : PrefsUtils.stringToArray(getPreferences().getString( key )) ) {
+ reps.add( findRepository(s) );
+ }
+ return new RepositorySet( reps );
+ }
+ else {
+ return null;
+ }
+ }
+
+ public Map<String, IRepositorySet> loadRepositorySets() {
+ IPreferenceStore store = getPreferences();
+
+ HashMap<String, IRepositorySet> sets = new HashMap<String, IRepositorySet>();
+
+ for ( String name : PrefsUtils.stringToArray(store.getString(REPOSITORY_SETS))) {
+ String key = REPOSITORY_SET + name;
+ ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+ for ( String s : PrefsUtils.stringToArray(getPreferences().getString( key )) ) {
+ reps.add( findRepository(s) );
+ }
+ sets.put( name, new RepositorySet( reps ) );
+ }
+
+ return sets;
+ }
+
+ public void saveRepositorySets(Map<String, IRepositorySet> sets) {
+ IPreferenceStore store = getPreferences();
+
+ ArrayList<String> names = new ArrayList<String>();
+
+ for ( Map.Entry<String, IRepositorySet> set : sets.entrySet() ) {
+ String name = set.getKey();
+ String key = REPOSITORY_SET + name;
+ ArrayList<String> ids = new ArrayList<String>();
+ for ( IRepositoryModel m : set.getValue().getRepositories() ) {
+ ids.add( m.getId() );
+ }
+ store.setValue(key, PrefsUtils.listToString(ids) );
+ names.add( name );
+ }
+
+ for ( String name : PrefsUtils.stringToArray(store.getString(REPOSITORY_SETS))) {
+ if ( !names.contains(name) ) {
+ String key = REPOSITORY_SET + name;
+ store.setToDefault(key);
+ }
+ }
+
+ store.setValue(REPOSITORY_SETS, PrefsUtils.listToString(names) );
+ timeStamp(store);
+ }
+
+ public void setDefaultRepositorySet(IRepositorySet defaultSet) {
+ ArrayList<String> ids = new ArrayList<String>();
+ for ( IRepositoryModel m : defaultSet.getRepositories() ) {
+ ids.add( m.getId() );
+ }
+ IPreferenceStore prefs = getPreferences();
+ prefs.setValue( REPOSITORY_DEFAULT_SET, PrefsUtils.listToString( ids ) );
+ timeStamp(prefs);
+ }
+
+ private void timeStamp(IPreferenceStore prefs) {
+ prefs.setValue( REPOSITORY_TIMESTAMP, System.currentTimeMillis() );
+ }
+
+ private IPreferenceStore getPreferences() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ private void deleteOldEntries(List<IRepositoryModel> repositories, IPreferenceStore prefs) {
+ for ( IRepositoryModel e : loadRepositories() ) {
+ if ( !repositories.contains(e) ) {
+ new File( makeFileName(e) ).delete();
+ String key = makeKey(e);
+ prefs.setToDefault( key + LOC );
+ prefs.setToDefault( key + NAME );
+ }
+ }
+
+ for ( IRepositoryType type : loadRepositoryTypes() ) {
+ boolean found = false;
+ for ( IRepositoryModel e : repositories ) {
+ if ( e.getType().equals( type ) ) {
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found ) {
+ prefs.setToDefault( REPOSITORY + type.getId() + INSTANCES );
+ }
+ }
+ }
+
+ private static void createNewEntries(HashMap<IRepositoryType, List<IRepositoryModel>> mapped, IPreferenceStore prefs) {
+ for ( Map.Entry<IRepositoryType, List<IRepositoryModel>> entry : mapped.entrySet() ) {
+ IRepositoryType type = entry.getKey();
+ if ( type.isDynamic() ) {
+ StringBuffer buf = new StringBuffer();
+
+ for ( IRepositoryModel element : entry.getValue() ) {
+ if ( buf.length() > 0 ) {
+ buf.append( "," );
+ }
+ buf.append( element.getId() );
+ saveRepository(element, prefs);
+ }
+
+ prefs.setValue( REPOSITORY + type.getId() + INSTANCES, buf.toString() );
+ }
+ else {
+ IRepositoryModel element = entry.getValue().get(0);
+ saveRepository(element, prefs);
+ }
+ }
+ }
+
+ private static void saveRepositoryPreferences(List<IRepositoryModel> repositories,
+ HashMap<IRepositoryType, List<IRepositoryModel>> mapped) throws CoreException {
+ for( IRepositoryModel rep : repositories ) {
+ try {
+ createDir( makeFileName(rep));
+ rep.getPreferences().save();
+ List<IRepositoryModel> list = mapped.get( rep.getType() );
+ if ( list == null ) {
+ list = new ArrayList<IRepositoryModel>(1);
+ mapped.put( rep.getType(), list );
+ }
+ list.add( rep );
+ } catch (IOException e) {
+ throw SigilCore.newCoreException("Failed to save repository preferences", e);
+ }
+ }
+ }
+
+ private static void createDir(String fileName) {
+ File file = new File( fileName );
+ file.getParentFile().mkdirs();
+ }
+
+ private static void saveRepository(IRepositoryModel element, IPreferenceStore prefs) {
+ String key = makeKey(element);
+ prefs.setValue( key + LOC, makeFileName(element) );
+ if ( element.getType().isDynamic() ) {
+ prefs.setValue( key + NAME, element.getName() );
+ }
+ prefs.setValue( key + TIMESTAMP, now() );
+ }
+
+ private static long now() {
+ return System.currentTimeMillis();
+ }
+
+ private static String makeKey(IRepositoryModel element) {
+ IRepositoryType type = element.getType();
+
+ String key = REPOSITORY + type.getId();
+ if ( type.isDynamic() )
+ key = key + "." + element.getId();
+
+ return key;
+ }
+
+ private static String makeFileName(IRepositoryModel element) {
+ IPath path = SigilCore.getDefault().getStateLocation();
+ path = path.append( "repository" );
+ path = path.append( element.getType().getId() );
+ path = path.append( element.getId() );
+ return path.toOSString();
+ }
+
+ private static RepositoryModel loadRepository(String id, String key, RepositoryType type, IPreferenceStore prefs) {
+ String name = type.isDynamic() ? prefs.getString( key + NAME ) : type.getType();
+
+ PreferenceStore repPrefs = new PreferenceStore();
+ RepositoryModel element = new RepositoryModel( id, name, type, repPrefs );
+
+ String loc = prefs.getString( key + LOC );
+
+ if ( loc == null || loc.trim().length() == 0 ) {
+ loc = makeFileName(element);
+ }
+
+ repPrefs.setFilename(loc);
+
+ if ( new File( loc ).exists() ) {
+ try {
+ repPrefs.load();
+ } catch (IOException e) {
+ SigilCore.error("Failed to load properties for repository " + key, e );
+ }
+ }
+
+ repPrefs.setValue( "id", id );
+
+ return element;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Image loadImage(IExtension ext, String icon) {
+ int i = icon.lastIndexOf( "/" );
+ String path = i == -1 ? "/" : icon.substring(0, i);
+ String name = i == -1 ? icon : icon.substring(i+1);
+
+ Bundle b = Platform.getBundle(ext.getContributor().getName());
+
+ Enumeration<URL> en = b.findEntries(path, name, false);
+ Image image = null;
+
+ if ( en.hasMoreElements() ) {
+ try {
+ image = SigilCore.loadImage(en.nextElement());
+ } catch (IOException e) {
+ SigilCore.error( "Failed to load image", e );
+ }
+ }
+ else {
+ SigilCore.error("No such image " + icon + " in bundle " + b.getSymbolicName() );
+ }
+
+ return image;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java
new file mode 100644
index 0000000..9b72541
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryModel.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.eclipse.jface.preference.PreferenceStore;
+
+public class RepositoryModel implements IRepositoryModel {
+ private String id;
+
+ private String name;
+
+ private IRepositoryType type;
+
+ private PreferenceStore preferences;
+
+ public RepositoryModel(String id, String name, IRepositoryType type, PreferenceStore preferences) {
+ this.id = id;
+ this.name = name;
+ this.type = type;
+ this.preferences = preferences;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getPreferences()
+ */
+ public PreferenceStore getPreferences() {
+ return preferences;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getType()
+ */
+ public IRepositoryType getType() {
+ return type;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getId()
+ */
+ public String getId() {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryModel#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.wizard.repository.IRepositoryModel#setName(java.lang.String)
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ try {
+ RepositoryModel e = (RepositoryModel) obj;
+ return id.equals(e.id);
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ public String toString() {
+ return type.getId() + ":" + id + ":" + name;
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java
new file mode 100644
index 0000000..30525a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/model/repository/RepositoryType.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.model.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.eclipse.swt.graphics.Image;
+
+public class RepositoryType implements IRepositoryType {
+ private String type;
+ private String id;
+ private Image icon;
+ private boolean dynamic;
+
+ public RepositoryType(String id, String type, boolean dynamic,
+ Image icon) {
+ this.id = id;
+ this.type = type;
+ this.dynamic = dynamic;
+ this.icon = icon;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getType()
+ */
+ public String getType() {
+ return type;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getId()
+ */
+ public String getId() {
+ return id;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#getIcon()
+ */
+ public Image getIcon() {
+ return icon;
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.preferences.repository.IRepositoryType#isDynamic()
+ */
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ try {
+ RepositoryType t = (RepositoryType) obj;
+ return t.id.equals( id );
+ }
+ catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return type;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java
new file mode 100644
index 0000000..c974644
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/GlobalRepositoryManager.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.internal.repository.eclipse.SigilRepositoryManager;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+
+public class GlobalRepositoryManager extends SigilRepositoryManager implements
+ IRepositoryManager {
+
+ public GlobalRepositoryManager() {
+ super(null);
+ }
+
+ @Override
+ protected IRepositoryModel[] findRepositories() {
+ List<IRepositoryModel> repos = SigilCore.getRepositoryConfiguration().loadRepositories();
+ return repos.toArray( new IRepositoryModel[repos.size()]);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java
new file mode 100644
index 0000000..186e962
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepository.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class OSGiInstallRepository extends AbstractBundleRepository {
+
+ private Map<String,List<ISigilBundle>> bundles;
+
+ public OSGiInstallRepository(String id) {
+ super(id);
+ }
+
+ public void refresh() {
+ synchronized( this ) {
+ bundles = null;
+ }
+
+ notifyChange();
+ }
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ IOSGiInstall install = SigilCore.getInstallManager().getDefaultInstall();
+
+ if ( install != null ) {
+ List<ISigilBundle> found = null;
+
+ synchronized( this ) {
+ found = bundles == null ? null : bundles.get( install.getId() );
+ }
+
+ if ( found == null ) {
+ found = new ArrayList<ISigilBundle>();
+ IPath source = install.getType().getSourceLocation();
+
+ for ( IPath p : install.getType().getDefaultBundleLocations() ) {
+ loadBundle( p, found, source );
+ }
+
+ synchronized( this ) {
+ bundles = new HashMap<String, List<ISigilBundle>>();
+ bundles.put( install.getId(), found );
+ }
+ }
+
+ for ( ISigilBundle b : found ) {
+ if ( !visitor.visit(b) ) {
+ break;
+ }
+ }
+ }
+ }
+
+ private void loadBundle(IPath p, List<ISigilBundle> bundles, IPath source) {
+ File f = p.toFile();
+ JarFile jar = null;
+ try {
+ jar = new JarFile(f);
+ ISigilBundle bundle = buildBundle(jar.getManifest(), f );
+ if ( bundle != null ) {
+ bundle.setLocation(p);
+ bundle.setSourcePathLocation( source );
+ bundle.setSourceRootPath( new Path( "src" ) );
+ bundles.add( bundle );
+ }
+ } catch (IOException e) {
+ BldCore.error( "Failed to read jar file " + f, e );
+ } catch (ModelElementFactoryException e) {
+ BldCore.error( "Failed to build bundle " + f , e );
+ } catch (RuntimeException e) {
+ BldCore.error( "Failed to build bundle " + f , e );
+ }
+ finally {
+ if ( jar != null ) {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ BldCore.error( "Failed to close jar file", e );
+ }
+ }
+ }
+ }
+
+ private ISigilBundle buildBundle(Manifest manifest, File f) {
+ IBundleModelElement info = buildBundleModelElement( manifest );
+
+ ISigilBundle bundle = null;
+
+ if ( info != null ) {
+ bundle = ModelElementFactory.getInstance().newModelElement( ISigilBundle.class );
+ bundle.addChild(info);
+ bundle.setLocation( new Path( f.getAbsolutePath() ) );
+ }
+
+ return bundle;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java
new file mode 100644
index 0000000..043eb81
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/OSGiInstallRepositoryProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+
+public class OSGiInstallRepositoryProvider implements IRepositoryProvider {
+ public IBundleRepository createRepository(String id, Properties preferences) {
+ return new OSGiInstallRepository(id);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java
new file mode 100644
index 0000000..0b4b757
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/SigilRepositoryManager.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.repository.AbstractRepositoryManager;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.cauldron.sigil.repository.RepositoryException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+public class SigilRepositoryManager extends AbstractRepositoryManager implements IRepositoryManager, IPropertyChangeListener {
+
+ private final String repositorySet;
+
+ private HashMap<String, RepositoryCache> cachedRepositories = new HashMap<String, RepositoryCache>();
+
+ class RepositoryCache {
+ private final Properties pref;
+ private final IBundleRepository repo;
+
+ RepositoryCache(Properties pref, IBundleRepository repo) {
+ this.pref = pref;
+ this.repo = repo;
+ }
+ }
+
+ public SigilRepositoryManager(String repositorySet) {
+ this.repositorySet = repositorySet;
+ }
+
+ @Override
+ public void initialise() {
+ super.initialise();
+ SigilCore.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+ }
+
+ public void destroy() {
+ IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+ if ( prefs != null ) {
+ prefs.removePropertyChangeListener(this);
+ }
+ }
+
+ @Override
+ protected void loadRepositories() {
+ IPreferenceStore prefs = SigilCore.getDefault().getPreferenceStore();
+
+ ArrayList<IBundleRepository> repos = new ArrayList<IBundleRepository>();
+ HashSet<String> ids = new HashSet<String>();
+
+ for ( IRepositoryModel repo : findRepositories() ) {
+ try {
+ IRepositoryProvider provider = findProvider( repo.getType() );
+ String id = repo.getId();
+ IBundleRepository repoImpl = null;
+ if ( repo.getType().isDynamic() ) {
+ String instance = "repository." + repo.getType().getId() + "." + id;
+ String loc = prefs.getString( instance + ".loc" );
+ Properties pref = loadPreferences(loc);
+ repoImpl = loadRepository(id, pref, provider);
+ }
+ else {
+ repoImpl = loadRepository(id, null, provider);
+ }
+
+ repos.add( repoImpl );
+ ids.add( id );
+ } catch (Exception e) {
+ SigilCore.error( "Failed to load repository for " + repo, e);
+ }
+ }
+
+ setRepositories(repos.toArray( new IBundleRepository[repos.size()] ) );
+
+ for ( Iterator<String> i = cachedRepositories.keySet().iterator(); i.hasNext(); ) {
+ if ( !ids.contains(i.next()) ) {
+ i.remove();
+ }
+ }
+ }
+
+ private IRepositoryProvider findProvider(IRepositoryType repositoryType) throws CoreException {
+ String id = repositoryType.getId();
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint p = registry.getExtensionPoint(SigilCore.REPOSITORY_PROVIDER_EXTENSION_POINT_ID);
+
+ for ( IExtension e : p.getExtensions() ) {
+ for ( IConfigurationElement c : e.getConfigurationElements() ) {
+ if ( id.equals( c.getAttribute("id") ) ) {
+ IRepositoryProvider provider = (IRepositoryProvider) c.createExecutableExtension("class");
+ return provider;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected IRepositoryModel[] findRepositories() {
+ if ( repositorySet == null ) {
+ return SigilCore.getRepositoryConfiguration().getDefaultRepositorySet().getRepositories();
+ }
+ else {
+ IRepositorySet set = SigilCore.getRepositoryConfiguration().getRepositorySet(repositorySet);
+ return set.getRepositories();
+ }
+ }
+
+ private IBundleRepository loadRepository(String id, Properties pref, IRepositoryProvider provider) throws RepositoryException {
+ try {
+ if ( pref == null ) {
+ pref = new Properties();
+ }
+
+ RepositoryCache cache = cachedRepositories.get(id);
+
+ if ( cache == null || !cache.pref.equals(pref) ) {
+ IBundleRepository repo = provider.createRepository(id, pref);
+ cache = new RepositoryCache(pref, repo);
+ cachedRepositories.put( id, cache );
+ }
+
+ return cache.repo;
+ } catch (RuntimeException e) {
+ throw new RepositoryException( "Failed to build repositories", e);
+ }
+ }
+
+ private Properties loadPreferences(String loc) throws FileNotFoundException, IOException {
+ FileInputStream in = null;
+ try {
+ Properties pref = new Properties();
+ pref.load(new FileInputStream(loc));
+ return pref;
+ }
+ finally {
+ if ( in != null ) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ SigilCore.error( "Failed to close file", e );
+ }
+ }
+ }
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if ( event.getProperty().equals( "repository.timestamp" ) ) {
+ loadRepositories();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java
new file mode 100644
index 0000000..d144e6f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepository.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.AbstractBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.CoreException;
+
+public class WorkspaceRepository extends AbstractBundleRepository implements IResourceChangeListener {
+
+ private static final int UPDATE_MASK = IResourceDelta.CONTENT | IResourceDelta.DESCRIPTION | IResourceDelta.OPEN;
+ private ISigilBundle[] bundles;
+
+ public WorkspaceRepository(String id) {
+ super(id);
+ }
+
+ @Override
+ public void accept(IRepositoryVisitor visitor, int options) {
+ synchronized( this ) {
+ if ( bundles == null ) {
+ List<ISigilProjectModel> models = SigilCore.getRoot().getProjects();
+ ArrayList<ISigilBundle> tmp = new ArrayList<ISigilBundle>(models.size());
+ for ( ISigilProjectModel n : models ) {
+ ISigilBundle b = n.getBundle();
+ tmp.add(b);
+ }
+ bundles = tmp.toArray( new ISigilBundle[tmp.size()] );
+ }
+ }
+
+ for ( ISigilBundle b : bundles ) {
+ visitor.visit(b);
+ }
+ }
+
+ public void refresh() {
+ synchronized(this) {
+ bundles = null;
+ }
+ }
+
+ @Override
+ protected void notifyChange() {
+ refresh();
+ super.notifyChange();
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ event.getDelta().accept( new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ boolean result;
+
+ IResource resource = delta.getResource();
+ if(resource instanceof IWorkspaceRoot) {
+ result = true;
+ } else if(resource instanceof IProject) {
+ IProject project = (IProject) resource;
+ if ( SigilCore.isSigilProject(project) ) {
+ switch (delta.getKind()) {
+ case IResourceDelta.CHANGED:
+ if ( (delta.getFlags() & UPDATE_MASK) == 0 ) {
+ break;
+ }
+ // else
+ // fall through on purpose
+ case IResourceDelta.ADDED: // fall through on purpose
+ case IResourceDelta.REMOVED: // fall through on purpose
+ notifyChange();
+ break;
+ }
+ result = true;
+ } else {
+ result = false;
+ }
+ } else if(resource.getName().equals(SigilCore.SIGIL_PROJECT_FILE)) {
+ switch(delta.getKind()) {
+ case IResourceDelta.CHANGED:
+ case IResourceDelta.ADDED:
+ case IResourceDelta.REMOVED:
+ notifyChange();
+ }
+ result = false;
+ } else {
+ result = false;
+ }
+ return result;
+ }
+ });
+ } catch (CoreException e) {
+ SigilCore.error( "Workspace repository update failed", e );
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java
new file mode 100644
index 0000000..b67aa25
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/internal/repository/eclipse/WorkspaceRepositoryProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.internal.repository.eclipse;
+
+import java.util.Properties;
+
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryProvider;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+public class WorkspaceRepositoryProvider implements IRepositoryProvider {
+ private static WorkspaceRepository repository;
+
+ public static WorkspaceRepository getWorkspaceRepository() {
+ return repository;
+ }
+
+ public IBundleRepository createRepository(String id, Properties preferences) {
+ if ( repository == null ) {
+ repository = new WorkspaceRepository(id);
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(repository, IResourceChangeEvent.POST_CHANGE);
+ }
+ return repository;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java
new file mode 100644
index 0000000..243dccf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ResolveProjectsJob.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.job;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IBundleResolver;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+public class ResolveProjectsJob extends Job {
+
+ private final IWorkspace workspace;
+ private final IProject project;
+
+ public ResolveProjectsJob(IWorkspace workspace) {
+ super("Resolving Sigil projects");
+ this.workspace = workspace;
+ this.project = null;
+ setRule(ResourcesPlugin.getWorkspace().getRoot());
+ }
+
+ public ResolveProjectsJob(IProject project) {
+ super("Resolving Sigil project");
+ this.workspace = null;
+ this.project = project;
+ setRule(project.getFile(SigilCore.SIGIL_PROJECT_FILE));
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ MultiStatus status = new MultiStatus(SigilCore.PLUGIN_ID, 0, "Error resolving Sigil projects", null);
+
+ Collection<ISigilProjectModel> sigilProjects = null;
+
+ if(workspace != null) {
+ sigilProjects = SigilCore.getRoot().getProjects();
+ } else if(project != null) {
+ try {
+ ISigilProjectModel sigilProject = SigilCore.create(project);
+ sigilProjects = Collections.singleton(sigilProject);
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ }
+
+ if ( sigilProjects != null ) {
+ for (ISigilProjectModel sigilProject : sigilProjects) {
+ try {
+ // Delete existing dependency markers on project
+ sigilProject.getProject().deleteMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE);
+
+ IRepositoryManager repository = SigilCore.getRepositoryManager(sigilProject);
+ ResolutionMonitorAdapter resolutionMonitor = new ResolutionMonitorAdapter(monitor);
+
+ IBundleResolver resolver = repository.getBundleResolver();
+ ResolutionConfig config = new ResolutionConfig(ResolutionConfig.IGNORE_ERRORS);
+
+ // Execute resolver
+ IResolution resolution = resolver.resolve(sigilProject, config, resolutionMonitor);
+
+ // Find missing imports
+ Set<IPackageImport> imports = sigilProject.getBundle().getBundleInfo().getImports();
+ for (IPackageImport pkgImport : imports) {
+ if(resolution.getProvider(pkgImport) == null) {
+ markMissingImport(pkgImport, sigilProject.getProject());
+ }
+ }
+
+ // Find missing required bundles
+ Set<IRequiredBundle> requiredBundles = sigilProject.getBundle().getBundleInfo().getRequiredBundles();
+ for (IRequiredBundle requiredBundle : requiredBundles) {
+ if(resolution.getProvider(requiredBundle) == null) {
+ markMissingRequiredBundle(requiredBundle, sigilProject.getProject());
+ }
+ }
+ } catch (ResolutionException e) {
+ status.add(new Status(IStatus.ERROR, SigilCore.PLUGIN_ID, 0, "Error resolving project " + sigilProject.getProject().getName(), e));
+ } catch (CoreException e) {
+ status.add(e.getStatus());
+ }
+ }
+ }
+
+ return status;
+ }
+
+ private static void markMissingImport(IPackageImport pkgImport, IProject project) throws CoreException {
+ IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE);
+ marker.setAttribute("element", pkgImport.getPackageName());
+ marker.setAttribute("versionRange", pkgImport.getVersions().toString());
+ marker.setAttribute(IMarker.MESSAGE, "Cannot resolve imported package \"" + pkgImport.getPackageName() + "\" with version range " + pkgImport.getVersions());
+ marker.setAttribute(IMarker.SEVERITY, pkgImport.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR);
+ marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+ }
+
+ private static void markMissingRequiredBundle(IRequiredBundle req, IProject project) throws CoreException {
+ IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE);
+ marker.setAttribute("element", req.getSymbolicName());
+ marker.setAttribute("versionRange", req.getVersions().toString());
+ marker.setAttribute(IMarker.MESSAGE, "Cannot resolve required bundle \"" + req.getSymbolicName() + "\" with version range " + req.getVersions());
+ marker.setAttribute(IMarker.SEVERITY, req.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR);
+ marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
+ }
+
+
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java
new file mode 100644
index 0000000..5c526d0
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/job/ThreadProgressMonitor.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.job;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class ThreadProgressMonitor {
+ private static ThreadLocal<IProgressMonitor> local = new ThreadLocal<IProgressMonitor>();
+
+ public static void setProgressMonitor(IProgressMonitor monitor) {
+ local.set(monitor);
+ }
+
+ public static IProgressMonitor getProgressMonitor() {
+ return local.get();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java
new file mode 100644
index 0000000..2c9b53a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilModelRoot.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.project;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public interface ISigilModelRoot {
+ List<ISigilProjectModel> getProjects();
+
+ Collection<ISigilProjectModel> resolveDependentProjects(ISigilProjectModel sigil, IProgressMonitor monitor);
+
+ Collection<ISigilBundle> resolveBundles(ISigilProjectModel sigil, IModelElement element, boolean includeOptional, IProgressMonitor monitor) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java
new file mode 100644
index 0000000..ebb8fe9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/project/ISigilProjectModel.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.project;
+
+import java.util.Collection;
+
+import org.cauldron.bld.config.IBldProject;
+import org.cauldron.bld.core.BldCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.osgi.framework.Version;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Represents a sigil project. To get a reference to a ISigilProjectModel you can use the
+ * helper method {@link BldCore#create(IProject)}.
+ *
+ * @author dave
+ *
+ */
+public interface ISigilProjectModel extends ICompoundModelElement {
+
+ /**
+ * @return
+ */
+ IProject getProject();
+
+ // shortcut to getProject().getName()
+ String getName();
+
+ Preferences getPreferences();
+
+ /**
+ *
+ * @param monitor
+ * The progress monitor to use for reporting progress to the
+ * user. It is the caller's responsibility to call done() on the
+ * given monitor. Accepts null, indicating that no progress
+ * should be reported and that the operation cannot be cancelled
+ * @throws CoreException
+ */
+ void save(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * @return
+ */
+ Version getVersion();
+
+ String getSymbolicName();
+
+ ISigilBundle getBundle();
+
+ void setBundle(ISigilBundle bundle);
+
+ /**
+ * @return
+ */
+ IJavaProject getJavaModel();
+
+ Collection<ISigilProjectModel> findDependentProjects(IProgressMonitor monitor);
+
+ void resetClasspath(IProgressMonitor monitor) throws CoreException;
+
+ IPath findBundleLocation() throws CoreException;
+
+ IModelElement findImport(String packageName, IProgressMonitor monitor);
+
+ boolean isInClasspath(String packageName, IProgressMonitor monitor) throws CoreException;
+
+ boolean isInClasspath(ISigilBundle bundle);
+
+ boolean isInBundleClasspath(IPackageFragmentRoot root) throws JavaModelException;
+
+ IPath findOutputLocation() throws CoreException;
+
+ IBldProject getBldProject() throws CoreException;
+
+ Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor) throws CoreException;
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java
new file mode 100644
index 0000000..7c62e6c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.internal.model.repository.RepositoryType;
+import org.eclipse.core.runtime.CoreException;
+
+public interface IRepositoryConfiguration {
+
+ List<IRepositoryModel> loadRepositories();
+
+ IRepositoryModel findRepository(String id);
+
+ void saveRepositories(List<IRepositoryModel> repositories) throws CoreException;
+
+ List<RepositoryType> loadRepositoryTypes();
+
+ IRepositoryModel newRepositoryElement(IRepositoryType type);
+
+ IRepositorySet getDefaultRepositorySet();
+
+ void setDefaultRepositorySet(IRepositorySet defaultSet);
+
+ IRepositorySet getRepositorySet(String name);
+
+ Map<String, IRepositorySet> loadRepositorySets();
+
+ void saveRepositorySets(Map<String, IRepositorySet> sets);
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java
new file mode 100644
index 0000000..182998f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryModel.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import org.eclipse.jface.preference.PreferenceStore;
+
+public interface IRepositoryModel {
+
+ String getId();
+
+ void setName(String stringValue);
+
+ String getName();
+
+ PreferenceStore getPreferences();
+
+ IRepositoryType getType();
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java
new file mode 100644
index 0000000..b4e644b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositorySet.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+public interface IRepositorySet {
+ void setRepository(IRepositoryModel id, int position);
+ void removeRepository(IRepositoryModel id);
+ IRepositoryModel[] getRepositories();
+ void setRepositories(IRepositoryModel[] repositories);
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java
new file mode 100644
index 0000000..33df100
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/IRepositoryType.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import org.eclipse.swt.graphics.Image;
+
+public interface IRepositoryType {
+
+ String getType();
+
+ String getId();
+
+ Image getIcon();
+
+ boolean isDynamic();
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java
new file mode 100644
index 0000000..5630a6d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/repository/RepositorySet.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class RepositorySet implements IRepositorySet {
+
+ private static final IRepositoryModel[] EMPTY = new IRepositoryModel[0];
+
+ private IRepositoryModel[] reps;
+
+ public RepositorySet() {
+ this( EMPTY );
+ }
+
+ public RepositorySet(Collection<IRepositoryModel> reps) {
+ this( reps.toArray( new IRepositoryModel[reps.size()] ) );
+ }
+
+ public RepositorySet(IRepositoryModel[] repositories) {
+ this.reps = repositories;
+ }
+
+ public void setRepository(IRepositoryModel id, int position) {
+ ArrayList<IRepositoryModel> tmp = new ArrayList<IRepositoryModel>(reps.length + 1);
+ tmp.remove(id);
+ tmp.add(position, id);
+ reps = tmp.toArray( new IRepositoryModel[tmp.size()] );
+ }
+
+ public IRepositoryModel[] getRepositories() {
+ return reps;
+ }
+
+ public void removeRepository(IRepositoryModel id) {
+ ArrayList<IRepositoryModel> tmp = new ArrayList<IRepositoryModel>(reps.length + 1);
+ tmp.remove(id);
+ reps = tmp.toArray( new IRepositoryModel[tmp.size()] );
+ }
+
+ public void setRepositories(IRepositoryModel[] repositories) {
+ reps = repositories == null ? EMPTY : repositories;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java
new file mode 100644
index 0000000..b2c7854
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/Grep.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+public class Grep {
+
+ // Pattern used to parse lines
+ private static Pattern linePattern = Pattern.compile(".*\r?\n");
+
+ // The input pattern that we're looking for
+ private Pattern pattern;
+
+ private CharBuffer cb;
+
+ private FileChannel fc;
+
+ private Grep(IFile f, Pattern pattern) throws IOException, CoreException {
+ this.pattern = pattern;
+ cb = openBuffer(f);
+ }
+
+ private CharBuffer openBuffer(IFile f) throws IOException, CoreException {
+ Charset charset = Charset.forName(f.getCharset());
+ CharsetDecoder decoder = charset.newDecoder();
+ // Open the file and then get a channel from the stream
+ FileInputStream fis = new FileInputStream(f.getLocation().toFile());
+ fc = fis.getChannel();
+
+ // Get the file's size and then map it into memory
+ int sz = (int) fc.size();
+ MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
+
+ // Decode the file into a char buffer
+ return decoder.decode(bb);
+ }
+
+ public static String[] grep(Pattern pattern, IFile...files) throws CoreException {
+ LinkedList<String> matches = new LinkedList<String>();
+ for ( IFile f : files ) {
+ try {
+ Grep g = new Grep( f, pattern );
+ g.grep(matches);
+ g.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ return matches.toArray( new String[matches.size()]);
+ }
+
+ private void close() throws IOException {
+ fc.close();
+ }
+
+ // Use the linePattern to break the given CharBuffer into lines, applying
+ // the input pattern to each line to see if we have a match
+ //
+ private void grep(List<String> matches) {
+ Matcher lm = linePattern.matcher(cb); // Line matcher
+ Matcher pm = null; // Pattern matcher
+ int lines = 0;
+ while (lm.find()) {
+ lines++;
+ CharSequence cs = lm.group(); // The current line
+ if (pm == null)
+ pm = pattern.matcher(cs);
+ else
+ pm.reset(cs);
+ if (pm.find())
+ matches.add(pm.group());
+ if (lm.end() == cb.limit())
+ break;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java
new file mode 100644
index 0000000..1c87c87
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/JavaHelper.java
@@ -0,0 +1,985 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.eclipse.ISCAComposite;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolution;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.core.runtime.content.IContentTypeManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IAccessRule;
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IClassFile;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IImportDeclaration;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.ILocalVariable;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IParent;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class JavaHelper {
+
+ public static final IAccessRule DENY_RULE = JavaCore.newAccessRule(
+ new Path("**"), IAccessRule.K_NON_ACCESSIBLE
+ | IAccessRule.IGNORE_IF_BETTER);
+
+ public static final IAccessRule ALLOW_ALL_RULE = JavaCore.newAccessRule(
+ new Path("**"), IAccessRule.K_ACCESSIBLE);
+
+ private static Map<String, Collection<IClasspathEntry>> entryCache = new HashMap<String, Collection<IClasspathEntry>>();
+
+ public static Collection<IClasspathEntry> findClasspathEntries(ISigilBundle bundle) {
+ LinkedList<IClasspathEntry> cp = new LinkedList<IClasspathEntry>();
+
+ ISigilProjectModel sp = bundle.getAncestor(ISigilProjectModel.class);
+
+ if ( sp != null ) {
+ IJavaProject p = sp.getJavaModel();
+
+ for ( String enc : bundle.getClasspathEntrys() ) {
+ IClasspathEntry e = p.decodeClasspathEntry(enc);
+ if ( e != null ) {
+ cp.add( e );
+ }
+ }
+ }
+
+ return cp;
+ }
+
+ public static Collection<ICompilationUnit> findCompilationUnits(ISigilProjectModel project) throws JavaModelException {
+ LinkedList<ICompilationUnit> ret = new LinkedList<ICompilationUnit>();
+
+ IJavaProject java = project.getJavaModel();
+ for ( IClasspathEntry cp : findClasspathEntries(project.getBundle()) ) {
+ IPackageFragmentRoot[] roots = java.findPackageFragmentRoots(cp);
+ for (IPackageFragmentRoot rt : roots ) {
+ for ( IJavaElement j : rt.getChildren() ) {
+ IPackageFragment p = (IPackageFragment) j;
+ ICompilationUnit[] units = p.getCompilationUnits();
+ for ( ICompilationUnit u : units ) {
+ ret.add( u );
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * @param project
+ * @param packageName
+ * @return
+ */
+ public static Collection<IPackageExport> findExportsForPackage(ISigilProjectModel project, final String packageName) {
+ final LinkedList<IPackageExport> results = new LinkedList<IPackageExport>();
+
+ SigilCore.getRepositoryManager(project).visit(new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageExport ) {
+ IPackageExport e = (IPackageExport) element;
+ if ( e.getPackageName().equals( packageName ) ) {
+ results.add( e );
+ }
+ }
+ return true;
+ }
+ });
+
+ return results;
+ }
+
+ public static String[] findUses(String packageName, ISigilProjectModel projectModel) throws CoreException {
+ ArrayList<String> uses = new ArrayList<String>();
+
+ for ( final String dependency : findPackageDependencies( packageName, projectModel ) ) {
+ if ( !dependency.equals( packageName ) ) {
+ final boolean[] found = new boolean[1];
+
+ projectModel.visit( new IModelWalker() {
+
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) element;
+ if ( pi.getPackageName().equals( dependency ) ) {
+ found[0] = true;
+ }
+ }
+ return !found[0];
+ }
+ } );
+
+ if ( found[0] ) {
+ uses.add( dependency );
+ }
+ }
+ }
+
+ return uses.toArray( new String[uses.size()] );
+ }
+
+ private static String[] findPackageDependencies(String packageName, ISigilProjectModel projectModel) throws CoreException {
+ HashSet<String> imports = new HashSet<String>();
+
+ IPackageFragment p = (IPackageFragment) projectModel.getJavaModel().findElement( new Path(packageName.replace('.', '/') ) );
+
+ if ( p == null ) {
+ throw SigilCore.newCoreException("Unknown package " + packageName, null);
+ }
+ for (ICompilationUnit cu : p.getCompilationUnits() ) {
+ scanImports(cu, imports);
+ }
+ for (IClassFile cf : p.getClassFiles() ) {
+ scanImports(cf, imports);
+ }
+
+ return imports.toArray( new String[imports.size()] );
+ }
+
+ /**
+ * @param project
+ * @param monitor
+ * @return
+ */
+ public static List<IPackageImport> findRequiredImports(ISigilProjectModel project, IProgressMonitor monitor) {
+ LinkedList<IPackageImport> imports = new LinkedList<IPackageImport>();
+
+ for ( String packageName : findJavaImports(project, monitor)) {
+ if ( !ProfileManager.isBootDelegate(project, packageName) ) { // these must come from boot classloader
+ try {
+ if ( !project.isInClasspath(packageName, monitor) ) {
+ Collection<IPackageExport> exports = findExportsForPackage(project, packageName);
+ if ( !exports.isEmpty() ) {
+ imports.add( select( exports ) );
+ }
+ }
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to check classpath", e );
+ }
+ }
+ }
+
+ return imports;
+ }
+
+ /**
+ * @param project
+ * @param monitor
+ * @return
+ */
+ public static Collection<IModelElement> findUnusedReferences(final ISigilProjectModel project, final IProgressMonitor monitor) {
+ final LinkedList<IModelElement> unused = new LinkedList<IModelElement>();
+
+ final Set<String> packages = findJavaImports(project, monitor);
+
+ project.visit( new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) element;
+ if ( !packages.contains( pi.getPackageName() ) ) {
+ unused.add( pi );
+ }
+ }
+ else if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) element;
+ IRepositoryManager manager = SigilCore.getRepositoryManager(project);
+ ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL | ResolutionConfig.IGNORE_ERRORS);
+ try {
+ IResolution r = manager.getBundleResolver().resolve(rb, config ,
+ new ResolutionMonitorAdapter(monitor) );
+ ISigilBundle bundle = r.getProvider(rb);
+ boolean found = false;
+ for ( IPackageExport pe : bundle.getBundleInfo().getExports() ) {
+ if ( packages.contains(pe.getPackageName() ) ) {
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found ) {
+ unused.add( rb );
+ }
+ } catch (ResolutionException e) {
+ SigilCore.error( "Failed to resolve " + rb, e );
+ }
+ }
+ return true;
+ }
+ });
+
+ return unused;
+ }
+
+ public static Collection<IClasspathEntry> resolveClasspathEntrys(ISigilProjectModel sigil, IProgressMonitor monitor)
+ throws CoreException {
+ if ( monitor == null ) {
+ monitor = Job.getJobManager().createProgressGroup();
+ monitor.beginTask("Resolving classpath for "
+ + sigil.getSymbolicName(), 2);
+ }
+
+ ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
+
+ ResolutionConfig config = new ResolutionConfig(ResolutionConfig.INCLUDE_OPTIONAL | ResolutionConfig.IGNORE_ERRORS | ResolutionConfig.INDEXED_ONLY | ResolutionConfig.LOCAL_ONLY);
+
+ IResolution resolution;
+ try {
+ resolution = SigilCore.getRepositoryManager(sigil).getBundleResolver().resolve(sigil, config, new ResolutionMonitorAdapter(monitor));
+ } catch (ResolutionException e) {
+ throw SigilCore.newCoreException("Failed to resolve dependencies", e);
+ }
+
+ monitor.worked(1);
+
+ for (ISigilBundle bundle : resolution.getBundles()) {
+ if (!bundle.getBundleInfo().getSymbolicName().equals(sigil.getSymbolicName())) { // discard self reference...
+ List<IModelElement> matched = resolution.getMatchedRequirements(bundle);
+ for (IClasspathEntry cpe : buildClassPathEntry(sigil, bundle, matched, monitor)) {
+ entries.add(cpe);
+ }
+ }
+ }
+
+ monitor.worked(1);
+ monitor.done();
+
+ return entries;
+ }
+
+ private static Collection<IClasspathEntry> buildClassPathEntry(ISigilProjectModel project, ISigilBundle provider, List<IModelElement> requirements, IProgressMonitor monitor) throws CoreException {
+ IAccessRule[] rules = buildAccessRules(project, provider, requirements);
+
+ ISigilProjectModel other = provider.getAncestor(ISigilProjectModel.class);
+
+ try {
+ if (other == null) {
+ provider.synchronize(monitor);
+ return newBundleEntry(provider, rules, null, false);
+ } else {
+ return newProjectEntry(other, rules, null, false);
+ }
+ } catch (IOException e) {
+ throw SigilCore.newCoreException("Failed to synchronize " + provider, e);
+ }
+ }
+
+ private static IAccessRule[] buildExportRules(ISigilBundle bundle) {
+ Set<IPackageExport> ex = bundle.getBundleInfo().getExports();
+ IAccessRule[] rules = new IAccessRule[ex.size() + 1];
+
+ Iterator<IPackageExport> iter = ex.iterator();
+ for (int i = 0; i < rules.length - 1; i++) {
+ IPackageExport p = iter.next();
+ rules[i] = JavaCore.newAccessRule(new Path(p.getPackageName()
+ .replace('.', '/')).append("*"), IAccessRule.K_ACCESSIBLE);
+ }
+
+ rules[rules.length - 1] = DENY_RULE;
+
+ return rules;
+ }
+
+ private static Collection<IClasspathEntry> newProjectEntry(
+ ISigilProjectModel n, IAccessRule[] rules,
+ IClasspathAttribute[] attributes, boolean export)
+ throws CoreException {
+ if (rules == null) {
+ rules = JavaHelper.buildExportRules(n.getBundle());
+ }
+
+ if (attributes == null) {
+ attributes = new IClasspathAttribute[] {};
+ }
+
+ ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
+ entries.add(JavaCore.newProjectEntry(n.getProject().getFullPath(),
+ rules, false, attributes, export));
+ for (IClasspathEntry e : n.getJavaModel().getRawClasspath()) {
+ switch (e.getEntryKind()) {
+ case IClasspathEntry.CPE_LIBRARY:
+ entries.add(JavaCore.newLibraryEntry(e.getPath(), e
+ .getSourceAttachmentPath(), e
+ .getSourceAttachmentRootPath(), rules, attributes,
+ export));
+ break;
+ case IClasspathEntry.CPE_VARIABLE:
+ IPath path = JavaCore.getResolvedVariablePath(e.getPath());
+ if (path != null) {
+ entries.add(JavaCore.newLibraryEntry(path, e
+ .getSourceAttachmentPath(), e
+ .getSourceAttachmentRootPath(), rules, attributes,
+ export));
+ }
+ break;
+ }
+ }
+
+ return entries;
+ }
+
+ private static Collection<IClasspathEntry> newBundleEntry(
+ ISigilBundle bundle, IAccessRule[] rules,
+ IClasspathAttribute[] attributes, boolean exported)
+ throws CoreException {
+ String name = bundle.getBundleInfo().getSymbolicName();
+
+ if (rules == null) {
+ rules = JavaHelper.buildExportRules(bundle);
+ }
+
+ if (attributes == null) {
+ attributes = new IClasspathAttribute[] {};
+ }
+
+ if (bundle.getBundleInfo().getVersion() != null) {
+ name += "_version_" + bundle.getBundleInfo().getVersion();
+ }
+
+ String cacheName = name + rules.toString();
+
+ Collection<IClasspathEntry> entries = null;
+
+ synchronized( entryCache ) {
+ entries = entryCache.get(cacheName);
+
+ if (entries == null) {
+ IPath path = bundle.getLocation();
+
+ if (path == null) {
+ SigilCore.error("Found null path for "
+ + bundle.getBundleInfo().getSymbolicName());
+ entries = Collections.emptyList();
+ } else {
+ entries = buildEntries(path, name, bundle, rules, attributes,
+ exported);
+ }
+
+ entryCache.put(cacheName, entries);
+ }
+ }
+
+ return entries;
+ }
+
+ private static IPath bundleCache = SigilCore.getDefault().getStateLocation().append(
+ "bundle-cache");
+
+ public static boolean isCachedBundle(String bp) {
+ return bp.startsWith(bundleCache.toOSString());
+ }
+
+ private static Collection<IClasspathEntry> buildEntries(IPath path,
+ String name, ISigilBundle bundle, IAccessRule[] rules,
+ IClasspathAttribute[] attributes, boolean exported)
+ throws CoreException {
+ if (path.toFile().isDirectory()) {
+ throw SigilCore.newCoreException("Bundle location cannot be a directory",
+ null);
+ } else {
+ // ok it's a jar could contain libs etc
+ try {
+ IPath cache = bundleCache.append(name);
+ Set<String> files = unpack(cache, bundle);
+ Set<String> classpath = filterClasspath(bundle.getBundleInfo().getClasspaths(), files);
+ ArrayList<IClasspathEntry> entries = new ArrayList<IClasspathEntry>(
+ classpath.size());
+
+ for (String cp : classpath) {
+ IPath p = ".".equals(cp) ? path : cache.append(cp);
+ IPath source = bundle.getSourcePathLocation();
+
+ if (source != null && !source.toFile().exists()) {
+ source = null;
+ }
+
+ IClasspathEntry e = JavaCore.newLibraryEntry(p, source,
+ bundle.getSourceRootPath(), rules, attributes,
+ exported);
+ entries.add(e);
+ }
+
+ return entries;
+ } catch (IOException e) {
+ throw SigilCore.newCoreException("Failed to unpack bundle", e);
+ }
+ }
+ }
+
+ private static Set<String> filterClasspath(Collection<String> classpaths,
+ Collection<String> files) {
+ HashSet<String> cp = new HashSet<String>(classpaths);
+ for (String c : cp) {
+ if (".".equals(c)) {
+ // ignore
+ } else {
+ if (!files.remove(c)) {
+ break;
+ }
+ }
+ }
+
+ if (files.isEmpty()) {
+ cp.remove(".");
+ }
+
+ return cp;
+ }
+
+ private static HashMap<IPath, Set<String>> unpacked = new HashMap<IPath, Set<String>>();
+
+ private static synchronized Set<String> unpack(IPath cache,
+ ISigilBundle bundle) throws IOException {
+ Set<String> files = unpacked.get(cache);
+
+ if ( files == null ) {
+ files = new HashSet<String>();
+ File dir = createEmptyDir(cache);
+ JarInputStream in = new JarInputStream(new FileInputStream(bundle
+ .getLocation().toFile()));
+ JarEntry entry;
+ while ((entry = in.getNextJarEntry()) != null) {
+ File f = new File(dir, entry.getName());
+ if (entry.isDirectory()) {
+ createDir(f);
+ } else {
+ try {
+ File p = f.getParentFile();
+ createDir(p);
+ streamTo(in, f);
+ files.add(entry.getName());
+ }
+ catch (RuntimeException e) {
+ SigilCore.error("Failed to unpack " + entry, e);
+ }
+ }
+ }
+
+ unpacked.put(cache, files);
+ }
+ return files;
+ }
+
+ private static void createDir(File p) throws IOException {
+ if (!p.exists()) {
+ if (!p.mkdirs())
+ throw new IOException("Failed to create directory " + p);
+ }
+ }
+
+ private static void streamTo(InputStream in, File f) throws IOException {
+ FileOutputStream fos = new FileOutputStream(f);
+ try {
+ byte[] buf = new byte[1024];
+ for (;;) {
+ int r = in.read(buf);
+
+ if (r == -1)
+ break;
+
+ fos.write(buf, 0, r);
+ }
+
+ fos.flush();
+ } finally {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ SigilCore.error("Failed to close stream", e);
+ }
+ }
+ }
+
+ private static File createEmptyDir(IPath cache) {
+ File dir = cache.toFile();
+ if (dir.exists()) {
+ deleteAll(dir);
+ }
+
+ dir.mkdirs();
+ return dir;
+ }
+
+ private static void deleteAll(File file) {
+ File[] sub = file.listFiles();
+ if (sub != null) {
+ for (File f : sub) {
+ deleteAll(f);
+ }
+ }
+ file.delete();
+ }
+
+ private static IAccessRule[] buildAccessRules(ISigilProjectModel project, ISigilBundle bundle, List<IModelElement> requirements) throws JavaModelException {
+ ArrayList<IAccessRule> rules = new ArrayList<IAccessRule>();
+
+ for (IModelElement e : requirements) {
+ if (e instanceof IRequiredBundle) {
+ IRequiredBundle host = project.getBundle().getBundleInfo().getFragmentHost();
+ if ( host != null ) {
+ if ( host.equals(e) ) {
+ return new IAccessRule[] { ALLOW_ALL_RULE };
+ }
+ else {
+ return buildExportRules(bundle);
+ }
+ }
+ else {
+ return buildExportRules(bundle);
+ }
+ } else if (e instanceof IPackageImport) {
+ IPackageImport pi = (IPackageImport) e;
+ String pckg = pi.getPackageName();
+ HashSet<String> pckgs = new HashSet<String>();
+ pckgs.add(pckg);
+ //findIndirectReferences(pckgs, pckg, project.getJavaModel(), project.getJavaModel());
+
+ for ( String p : pckgs ) {
+ rules.add(newPackageAccess(p));
+ }
+ }
+ }
+
+ rules.add(DENY_RULE);
+
+ return rules.toArray(new IAccessRule[rules.size()]);
+ }
+
+ /*
+ * Searches for C (and D, E, etc) in case:
+ * A extends B extends C where A, B and C are in different packages and A is in this bundle
+ * and B and C are in one or more external bundles
+ */
+ private static void findIndirectReferences(Set<String> indirect, String pckg, IParent parent, IJavaProject p) throws JavaModelException {
+ for ( IJavaElement e : parent.getChildren() ) {
+ boolean skip = false;
+ switch ( e.getElementType() ) {
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ IPackageFragmentRoot rt = (IPackageFragmentRoot) e;
+ IClasspathEntry ce = rt.getRawClasspathEntry();
+ IPath path = ce.getPath();
+ skip = "org.eclipse.jdt.launching.JRE_CONTAINER".equals(path.toString());
+ break;
+ case IJavaElement.CLASS_FILE:
+ IClassFile cf = (IClassFile) e;
+ if ( cf.getElementName().startsWith(pckg) ) {
+ findIndirectReferences(indirect, findPackage(cf.getType().getSuperclassName()), p, p);
+ }
+ break;
+ case IJavaElement.COMPILATION_UNIT:
+ ICompilationUnit cu = (ICompilationUnit) e;
+ break;
+ }
+
+ if ( !skip && e instanceof IParent ) {
+ IParent newParent = (IParent) e;
+ findIndirectReferences(indirect, pckg, newParent, p);
+ }
+ }
+ }
+
+ private static IAccessRule newPackageAccess(String packageName) {
+ return JavaCore.newAccessRule(new Path(packageName.replace('.', '/'))
+ .append("*"), IAccessRule.K_ACCESSIBLE);
+ }
+
+ private static Set<String> findJavaImports(ISigilProjectModel project, IProgressMonitor monitor) {
+ Set<String> imports = new HashSet<String>();
+
+ findJavaModelImports(project, imports, monitor);
+ findSCAImports(project, imports, monitor);
+ findTextImports(project, imports, monitor);
+
+ return imports;
+ }
+
+ private static void findSCAImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+ for ( ISCAComposite sca : project.getBundle().getComposites() ) {
+ IFile f = project.getProject().getFile(sca.getLocation());
+ if ( f.exists() ) {
+ try {
+ // TODO for now just treats sca as text files - should build in richer model that is able to detect java elements
+ parseText( f, imports );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to parse sca file " + f, e );
+ }
+ }
+ }
+ }
+
+ private static void findTextImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+ IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
+ IContentType txt = contentTypeManager.getContentType("org.eclipse.core.runtime.text");
+ for ( IPath p : project.getBundle().getSourcePaths() ) {
+ IFile f = project.getProject().getFile(p);
+ if ( f.exists() ) {
+ try {
+ IContentDescription desc = f.getContentDescription();
+ if ( desc != null ) {
+ IContentType type = desc.getContentType();
+ if ( type != null ) {
+ if ( type.isKindOf( txt ) ) {
+ parseText( f, imports );
+ }
+ }
+ }
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to parse text file " + f, e );
+ }
+ }
+ }
+ }
+
+ private static void findJavaModelImports(ISigilProjectModel project, Set<String> imports, IProgressMonitor monitor) {
+ try {
+ for ( IPackageFragment root : project.getJavaModel().getPackageFragments() ) {
+ IPackageFragmentRoot rt = (IPackageFragmentRoot) root.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+
+ if ( isInClassPath( project, rt ) ) {
+ for ( ICompilationUnit cu : root.getCompilationUnits() ) {
+ scanImports( cu, imports );
+ }
+
+ for ( IClassFile cf : root.getClassFiles() ) {
+ scanImports( cf, imports );
+ }
+ }
+ }
+ } catch (JavaModelException e) {
+ SigilCore.error( "Failed to parse java model", e );
+ }
+ }
+
+ // matches word.word.word.word.Word
+ private static final Pattern JAVA_CLASS_PATTERN = Pattern.compile("((\\w*\\.\\w*)+?)\\.[A-Z]\\w*");
+
+ private static void parseText(IFile f, Set<String> imports) throws CoreException {
+ for ( String result : Grep.grep( JAVA_CLASS_PATTERN, f) ) {
+ findImport(result, imports);
+ }
+ }
+
+ private static boolean isInClassPath(ISigilProjectModel project, IPackageFragmentRoot rt) throws JavaModelException {
+ String path = encode( project, rt.getRawClasspathEntry() );
+ return project.getBundle().getClasspathEntrys().contains( path );
+ }
+
+ private static String encode(ISigilProjectModel project, IClasspathEntry cp) {
+ return project.getJavaModel().encodeClasspathEntry(cp).trim();
+ }
+
+ private static void scanImports(IParent parent, Set<String> imports) throws JavaModelException {
+ for ( IJavaElement e : parent.getChildren() ) {
+ switch ( e.getElementType() ) {
+ case IJavaElement.TYPE:
+ handleType( (IType) e, imports );
+ break;
+ case IJavaElement.IMPORT_DECLARATION:
+ handleImport( (IImportDeclaration) e, imports );
+ break;
+ case IJavaElement.FIELD:
+ handleField( (IField) e, imports );
+ break;
+ case IJavaElement.LOCAL_VARIABLE:
+ handleLocalVariable( (ILocalVariable) e, imports );
+ break;
+ case IJavaElement.ANNOTATION:
+ handleAnnotation( (IAnnotation) e, imports );
+ break;
+ case IJavaElement.METHOD:
+ handleMethod( (IMethod) e, imports );
+ break;
+ default:
+ // no action
+ break;
+ }
+
+ if ( e instanceof IParent ) {
+ scanImports((IParent) e, imports);
+ }
+ }
+ }
+
+ private static void handleType(IType e, Set<String> imports) throws JavaModelException {
+ findImportFromType(e.getSuperclassTypeSignature(), imports);
+ for ( String sig : e.getSuperInterfaceTypeSignatures() ) {
+ findImportFromType(sig, imports);
+ }
+ //findImportsForSuperTypes(e, imports);
+ }
+
+ /*private static void findImportsForSuperTypes(IType e, Set<String> imports) throws JavaModelException {
+ IJavaProject project = (IJavaProject) e.getAncestor(IJavaModel.JAVA_PROJECT);
+ LinkedList<String> types = new LinkedList<String>();
+ types.add( decodeSignature(e.getSuperclassTypeSignature()) );
+ for ( String sig : e.getSuperInterfaceTypeSignatures() ) {
+ types.add( decodeSignature(sig) );
+ }
+
+ for ( IPackageFragmentRoot root : project.getPackageFragmentRoots() ) {
+ // only need to search binary files for inheritance as source will automatically be searched
+ if ( root.getKind() == IPackageFragmentRoot.K_BINARY ) {
+ for ( String t : types ) {
+ String pac = findPackage(t);
+ if ( pac != null ) {
+ IPackageFragment fragment = root.getPackageFragment(pac);
+ if ( fragment != null ) {
+ IClassFile c = fragment.getClassFile(findClass(t));
+ if ( c != null ) {
+ findImportsForSuperTypes(c.getType(), imports);
+ }
+ }
+ }
+ }
+ }
+ }
+ } */
+
+ private static void handleMethod(IMethod e, Set<String> imports) throws JavaModelException {
+ findImportFromType(e.getReturnType(), imports);
+
+ for ( String param : e.getParameterTypes() ) {
+ findImportFromType(param, imports);
+ }
+
+ for ( String ex : e.getExceptionTypes() ) {
+ findImportFromType( ex, imports );
+ }
+ }
+
+ private static void handleAnnotation(IAnnotation e, Set<String> imports) {
+ findImport(e.getElementName(), imports);
+ }
+
+ private static void handleLocalVariable(ILocalVariable e, Set<String> imports) {
+ findImportFromType(e.getTypeSignature(), imports);
+ }
+
+ private static void handleField(IField e, Set<String> imports) throws IllegalArgumentException, JavaModelException {
+ findImportFromType(Signature.getElementType(e.getTypeSignature()), imports);
+ }
+
+ private static void handleImport(IImportDeclaration id, Set<String> imports) {
+ findImport( id.getElementName(), imports);
+ }
+
+ private static void findImportFromType(String type, Set<String> imports) {
+ String element = decodeSignature(type);
+ findImport(element, imports);
+ }
+
+ private static String decodeSignature(String type) {
+ return decodeSignature(type, false);
+ }
+
+ private static String decodeSignature(String type, boolean resolve) {
+ if ( type == null ) {
+ return null;
+ }
+
+ if ( type.length() > 0 ) {
+ switch ( type.charAt(0) ) {
+ case Signature.C_ARRAY:
+ return decodeSignature(type.substring(1) );
+ case Signature.C_UNRESOLVED:
+ return resolve ? resolve( type.substring(1, type.length() - 1) ) : null;
+ case Signature.C_RESOLVED:
+ return type.substring(1);
+ }
+ }
+ return type;
+ }
+
+ private static String resolve(String substring) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private static void findImport(String clazz, Set<String> imports) {
+ String packageName = findPackage(clazz);
+ if ( packageName != null ) {
+ imports.add(packageName);
+ }
+ }
+
+ private static String findPackage(String clazz) {
+ if ( clazz == null ) return null;
+ int pos = clazz.lastIndexOf( '.' );
+ return pos == -1 ? null : clazz.substring(0, pos);
+ }
+
+ private static String findClass(String clazz) {
+ if ( clazz == null ) return null;
+ int pos = clazz.lastIndexOf( '.' );
+ return pos == -1 ? null : clazz.substring(pos + 1);
+ }
+
+ private static IPackageImport select(Collection<IPackageExport> proposals) {
+ IPackageExport pe = null;
+
+ for ( IPackageExport check : proposals ) {
+ if ( pe == null || check.getVersion().compareTo( pe.getVersion() ) > 0 ) {
+ pe = check;
+ }
+ }
+
+ String packageName = pe.getPackageName();
+
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = pe.getVersion();
+ VersionRange versions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName(packageName);
+ pi.setVersions(versions);
+
+ return pi;
+ }
+
+ public static Iterable<IJavaElement> findTypes(IParent parent, int... type) throws JavaModelException {
+ LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
+ scanForElement(parent, type, found, false);
+ return found;
+ }
+
+ public static IJavaElement findType(IParent parent, int... type) throws JavaModelException {
+ LinkedList<IJavaElement> found = new LinkedList<IJavaElement>();
+ scanForElement(parent, type, found, true);
+ return found.isEmpty() ? null : found.getFirst();
+ }
+
+ private static void scanForElement(IParent parent, int[] type, List<IJavaElement> roots, boolean thereCanBeOnlyOne) throws JavaModelException {
+ for ( IJavaElement e : parent.getChildren() ) {
+ if ( isType(type, e) ) {
+ roots.add( e );
+ if ( thereCanBeOnlyOne ) {
+ break;
+ }
+ }
+ else if ( e instanceof IParent ) {
+ scanForElement( (IParent) e, type, roots, thereCanBeOnlyOne );
+ }
+ }
+ }
+
+ private static boolean isType(int[] type, IJavaElement e) {
+ for ( int i : type ) {
+ if ( i == e.getElementType() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isAssignableTo(String ifaceOrParentClass, IType type) throws JavaModelException {
+ if ( ifaceOrParentClass == null ) return true;
+
+ ITypeHierarchy h = type.newSupertypeHierarchy(null);
+
+ for ( IType superType : h.getAllClasses() ) {
+ String name = superType.getFullyQualifiedName();
+ if ( name.equals( ifaceOrParentClass ) ) {
+ return true;
+ }
+ }
+ for ( IType ifaceType : h.getAllInterfaces() ) {
+ String name = ifaceType.getFullyQualifiedName();
+ if ( name.equals( ifaceOrParentClass ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static IType findType(ITypeRoot root) throws JavaModelException {
+ // TODO Auto-generated method stub
+ for ( IJavaElement child : root.getChildren() ) {
+ if ( child.getElementType() == IJavaElement.TYPE ) {
+ return (IType) child;
+ }
+ }
+
+ throw new JavaModelException( new IllegalStateException( "Missing type for " + root) , IStatus.ERROR );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java
new file mode 100644
index 0000000..a9f5017
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ModelHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class ModelHelper {
+ public static List<IModelElement> findUsers(IModelElement e) {
+ LinkedList<IModelElement> users = new LinkedList<IModelElement>();
+
+ findUsers(e, users);
+
+ return users;
+ }
+
+ private static void findUsers(IModelElement e, final LinkedList<IModelElement> users) {
+ if ( e instanceof IPackageExport ) {
+ final IPackageExport pe = (IPackageExport) e;
+ SigilCore.getGlobalRepositoryManager().visit( new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) element;
+ if ( pi.accepts( pe ) ) {
+ users.add( pi );
+ }
+ return false;
+ }
+
+ return true;
+ }
+ } );
+ }
+ else if ( e instanceof IBundleModelElement ) {
+ final IBundleModelElement bndl = (IBundleModelElement) e;
+
+ SigilCore.getGlobalRepositoryManager().visit( new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle req = (IRequiredBundle) element;
+ if ( req.accepts( bndl ) ) {
+ users.add( req );
+ }
+ return false;
+ }
+ return true;
+ }
+ } );
+ }
+
+ if ( e instanceof ICompoundModelElement ) {
+ ICompoundModelElement c = (ICompoundModelElement) e;
+ IModelElement[] ch = c.children();
+ for ( IModelElement ee : ch ) {
+ findUsers( ee, users );
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java
new file mode 100644
index 0000000..b42287c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/model/util/ProfileManager.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.model.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.core.runtime.Platform;
+import org.osgi.framework.Bundle;
+
+public class ProfileManager {
+ private static final Pattern[] BOOT_DELEGATION_PATTERNS = new Pattern[] {
+ GlobCompiler.compile("org.ietf.jgss"),
+ GlobCompiler.compile("org.omg.*"),
+ GlobCompiler.compile("org.w3c.*"),
+ GlobCompiler.compile("org.xml.*"),
+ GlobCompiler.compile("sun.*"),
+ GlobCompiler.compile("com.sun.*"),
+ };
+
+ private static HashMap<String, Properties> profiles;
+
+ public static boolean isBootDelegate(ISigilProjectModel project, String packageName) {
+ if ( packageName.startsWith( "java." ) ) {
+ return true;
+ }
+
+ for ( Pattern p : BOOT_DELEGATION_PATTERNS ) {
+ if ( p.matcher(packageName).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Properties findProfileForVersion(String javaVersion) {
+ Map<String, Properties> profiles = loadProfiles();
+
+ if ( "1.5.0".equals( javaVersion ) ) {
+ return profiles.get( "J2SE-1.5" );
+ }
+ else if ( "1.6.0".equals( javaVersion ) ) {
+ return profiles.get( "J2SE-1.6" );
+ }
+
+ return null;
+ }
+
+ private synchronized static Map<String, Properties> loadProfiles() {
+ if ( profiles == null ) {
+ profiles = new HashMap<String, Properties>();
+
+ Bundle b = Platform.getBundle("org.eclipse.osgi");
+
+ for ( String profile : loadProfiles( b )) {
+ if ( profile.trim().length() > 0 ) {
+ URL url = findURL(profile, b);
+ if ( url != null ) {
+ try {
+ Properties p = loadProperties(url);
+ String name = p.getProperty("osgi.java.profile.name");
+ if ( name != null ) {
+ profiles.put(name, p);
+ }
+ else {
+ SigilCore.error( "Invalid profile definition, no name specified: " + url);
+ }
+ } catch (IOException e) {
+ SigilCore.error( "Failed to load java profile", e );
+ }
+ }
+ else {
+ SigilCore.error( "Unknown profile **" + profile + "**" );
+ }
+ }
+ // else ignore empty values
+ }
+ }
+ return profiles;
+ }
+
+ private static String[] loadProfiles(Bundle b) {
+ URL url = findURL("profile.list", b);
+
+ if ( url != null ) {
+ try {
+ Properties p = loadProperties(url);
+ String s = p.getProperty("java.profiles");
+ return s == null ? new String[] {} : s.split(",");
+ } catch (IOException e) {
+ SigilCore.error( "Failed to load java profile list", e );
+ }
+ }
+ else {
+ SigilCore.error( "Failed to find java profile list" );
+ }
+
+ // fine no properties found
+ return new String[] {};
+ }
+
+ @SuppressWarnings("unchecked")
+ private static URL findURL(String file, Bundle b) {
+ Enumeration e = b.findEntries("/", file, false);
+ return e == null ? null : (URL) (e.hasMoreElements() ? e.nextElement() : null);
+ }
+
+
+ private static Properties loadProperties(URL url) throws IOException {
+ Properties p = new Properties();
+
+ InputStream in = null;
+
+ try {
+ in = url.openStream();
+ p.load(in);
+ }
+ finally {
+ if ( in != null ) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return p;
+ }
+
+
+
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java
new file mode 100644
index 0000000..b425fa2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/nature/SigilProjectNature.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.nature;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+public class SigilProjectNature implements IProjectNature {
+
+ private IProject project;
+
+ public void configure() throws CoreException {
+ // TODO configure project builder
+
+ }
+
+ public void deconfigure() throws CoreException {
+ }
+
+ public IProject getProject() {
+ return project;
+ }
+
+ public void setProject(IProject project) {
+ this.project = project;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java
new file mode 100644
index 0000000..0635220
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PrefsUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.text.StrTokenizer;
+
+public class PrefsUtils {
+
+ private PrefsUtils() {
+ }
+
+ public static final String arrayToString(String[] array) {
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = 0; i < array.length; i++) {
+ if (i > 0)
+ builder.append(',');
+ builder.append(StringEscapeUtils.escapeCsv(array[i]));
+ }
+
+ return builder.toString();
+ }
+
+ public static final String[] stringToArray(String string) {
+ StrTokenizer tokenizer = new StrTokenizer(string, ',', '"');
+ String[] array = new String[tokenizer.size()];
+
+ for (int i = 0; i < array.length; i++) {
+ array[i] = tokenizer.nextToken();
+ }
+
+ return array;
+ }
+
+ public static String listToString(List<String> names) {
+ return arrayToString(names.toArray( new String[names.size()]));
+ }
+
+ public static List<String> stringToList(String string) {
+ return new ArrayList<String>(Arrays.asList(stringToArray(string)));
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java
new file mode 100644
index 0000000..4feb070
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/PromptablePreference.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+public enum PromptablePreference {
+ Always, Prompt, Never
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java
new file mode 100644
index 0000000..6d2e1e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/preferences/SigilPreferencesInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.preferences;
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.internal.model.repository.RepositoryConfiguration;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class SigilPreferencesInitializer extends AbstractPreferenceInitializer {
+
+ public static final String[] EXCLUDED_RESOURCES = new String[] {
+ ".project", ".classpath", ".settings"
+ };
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+
+ store.setDefault(SigilCore.OSGI_INSTALL_CHECK_PREFERENCE, true);
+
+ store.setDefault(SigilCore.DEFAULT_VERSION_LOWER_BOUND, VersionRangeBoundingRule.Micro.name());
+ store.setDefault(SigilCore.DEFAULT_VERSION_UPPER_BOUND, VersionRangeBoundingRule.Any.name());
+
+ store.setDefault(SigilCore.DEFAULT_EXCLUDED_RESOURCES, PrefsUtils.arrayToString(EXCLUDED_RESOURCES));
+
+ store.setDefault(SigilCore.PREFERENCES_NOASK_OSGI_INSTALL, false);
+
+ store.setDefault(SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, PromptablePreference.Prompt.name());
+
+ store.setDefault(SigilCore.PREFERENCES_REBUILD_PROJECTS, PromptablePreference.Prompt.name() );
+
+ store.setDefault(RepositoryConfiguration.REPOSITORY_DEFAULT_SET, "org.cauldron.sigil.core.workspaceprovider" );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java
new file mode 100644
index 0000000..ae85568
--- /dev/null
+++ b/sigil/org.cauldron.sigil.core/src/org/cauldron/sigil/property/SigilPropertyTester.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.property;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+
+public class SigilPropertyTester extends PropertyTester {
+
+ public SigilPropertyTester() {
+ }
+
+ public boolean test( Object receiver, String property, Object[] args, Object expectedValue ) {
+ IResource resource = (IResource) receiver;
+ if ( "isSigilProject".equals( property ) ) {
+ return expectedValue.equals( isSigilProjectLikeResource( resource ) );
+ }
+ return false;
+ }
+
+ /**
+ * @param resource
+ * @return
+ */
+ private static boolean isSigilProjectLikeResource(IResource resource) {
+ if ( resource instanceof IProject ) {
+ IProject p = (IProject) resource;
+ return SigilCore.isSigilProject(p);
+ }
+ else {
+ return resource.getName().equals( SigilCore.SIGIL_PROJECT_FILE );
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.feature/.project b/sigil/org.cauldron.sigil.feature/.project
new file mode 100644
index 0000000..6d3d7cf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.feature/build.properties b/sigil/org.cauldron.sigil.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/sigil/org.cauldron.sigil.feature/feature.xml b/sigil/org.cauldron.sigil.feature/feature.xml
new file mode 100644
index 0000000..9e3dc15
--- /dev/null
+++ b/sigil/org.cauldron.sigil.feature/feature.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<feature
+ id="org.cauldron.sigil.feature"
+ label="Sigil SDK"
+ version="0.8.0.qualifier"
+ provider-name="Paremus Ltd">
+
+ <description>
+ Sigil plugin, allow developers to build OSGi applications for deployment in newton within eclipse.
+ </description>
+
+ <copyright>
+ Copyright 2009 Paremus Limited, London, United Kingdom
+ </copyright>
+
+ <license url="http://www.apache.org/licenses/LICENSE-2.0">
+ Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+ </license>
+
+ <url>
+ <update url="http://replace.with.real.url"/>
+ </url>
+
+ <requires>
+ <import plugin="org.eclipse.ant.core"/>
+ <import plugin="org.eclipse.core.expressions"/>
+ <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.debug.ui"/>
+ <import plugin="org.eclipse.jdt.core"/>
+ <import plugin="org.eclipse.jdt.ui"/>
+ <import plugin="org.eclipse.jface.text"/>
+ <import plugin="org.eclipse.pde.ui"/>
+ <import plugin="org.eclipse.ui.editors"/>
+ <import plugin="org.eclipse.ui.forms"/>
+ <import plugin="org.eclipse.ui.ide"/>
+ <import plugin="org.eclipse.ui.workbench.texteditor"/>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.osgi"/>
+ <import plugin="org.eclipse.jdt.launching"/>
+ <import plugin="org.eclipse.ui.console"/>
+ <import plugin="org.eclipse.jdt.debug.ui"/>
+ <import plugin="org.eclipse.ui.editors" version="3.4.0" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.jface.text" version="3.4.0" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.ui.ide" version="3.4.0" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.help"/>
+ <import plugin="org.eclipse.zest.core" version="1.0.0" match="compatible"/>
+ <import plugin="org.eclipse.zest.layouts" version="1.0.0" match="compatible"/>
+ <import plugin="org.eclipse.ltk.core.refactoring"/>
+ <import plugin="org.eclipse.help" version="3.3.1" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.ui.cheatsheets" version="3.3.100" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.equinox.common" version="3.4.0" match="greaterOrEqual"/>
+ <import plugin="org.cauldron.sigil.ui" version="0.5.5" match="greaterOrEqual"/>
+ <import plugin="org.cauldron.sigil.core" version="0.5.5" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="org.cauldron.sigil.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.sigil.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.sigil.help"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.sigil.utils"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.bld.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.sigil.search"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.help/.classpath b/sigil/org.cauldron.sigil.help/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.help/.project b/sigil/org.cauldron.sigil.help/.project
new file mode 100644
index 0000000..3e3bb3b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.help</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1174b4b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Fri Aug 01 14:46:19 BST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..97cd5ab
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Help Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.help;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.help.Activator
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.help;bundle-version="3.3.1",
+ org.eclipse.ui.cheatsheets;bundle-version="3.3.100",
+ org.cauldron.sigil.ui;bundle-version="0.5.5",
+ org.eclipse.ui.ide;bundle-version="3.4.0",
+ org.cauldron.sigil.core;bundle-version="0.5.5"
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+Import-Package: org.cauldron.sigil.model.eclipse,
+ org.eclipse.core.resources
diff --git a/sigil/org.cauldron.sigil.help/build.properties b/sigil/org.cauldron.sigil.help/build.properties
new file mode 100644
index 0000000..5bab7a8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ html/,\
+ toc.xml,\
+ cheatsheets/,\
+ archive/
diff --git a/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml b/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml
new file mode 100644
index 0000000..94ce68d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/cheatsheets/HelloWorld.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<cheatsheet title="Create a Hello World composite application">
+ <intro>
+ <description>This cheat sheet shows you how to create a simple hello world composite application using Sigil.</description>
+ </intro>
+
+ <item title="Create an empty Newton project" href="/org.cauldron.sigil.help/html/tasks/new_project.html" dialog="true" skip="true">
+ <description>
+ First, you must create a new "org.example.helloworld" empty Newton project to house the java classes and SCA composites.
+ </description>
+
+ <subitem label="Select "File >New >Project...". Expand "Sigil", select "Newton Project" and click "Next"." skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.OpenEmptyNewtonProjectWizardAction"
+ param1="org.example.helloworld"
+ translate="" />
+ </subitem>
+
+ <subitem label="Enter "org.example.helloworld" as the "Project name", then click "Finish"." skip="false" />
+ </item>
+
+ <item title="Create The Java Interfaces and Classes" skip="true">
+ <description>
+ Create a new "org.example.helloworld.api" package and add a HelloWorld interface to this package.
+ Then create a new "org.example.helloworld.impl" package and add a HelloWorldImpl class which implements HelloWorld to the impl package.
+ Finally create a new "org.example.helloworld.cli" package and add a HelloWorldCli class which references the HelloWorld api to the cli package.
+ </description>
+
+ <subitem label="Create the HelloWorld API" skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+ param1="org.example.helloworld"
+ param2="src/org/example/helloworld/api"
+ param3="org.cauldron.sigil.help"
+ param4="archive/java/HelloWorld.java"
+ param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+ translate="" />
+ </subitem>
+ <subitem label="Create the HelloWorld Impl" skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+ param1="org.example.helloworld"
+ param2="src/org/example/helloworld/impl"
+ param3="org.cauldron.sigil.help"
+ param4="archive/java/HelloWorldImpl.java"
+ param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+ translate="" />
+ </subitem>
+ <subitem label="Create the HelloWorld CLI" skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+ param1="org.example.helloworld"
+ param2="src/org/example/helloworld/cli"
+ param3="org.cauldron.sigil.help"
+ param4="archive/java/HelloWorldCli.java"
+ param5="org.eclipse.jdt.ui.CompilationUnitEditor"
+ translate="" />
+ </subitem>
+ </item>
+ <item
+ title="Add OSGi Package Imports To The HelloWorld Project">
+ <description>
+ The HelloWorldCli class references classes which are exported from other OSGi bundles. In order to satisfy the OSGi classloading rules the HelloWorld project must import the required packages.
+ </description>
+ <subitem
+ label="Open the "org.example.helloworld" project editor by double-clicking on the "sigil.properties" file in the project directory."
+ skip="false">
+ </subitem>
+ <subitem
+ label="Select the "Overview" tab.">
+ </subitem>
+ <subitem
+ label="Click on the "Resolve missing dependencies" link in the "Tools" section.">
+ </subitem>
+ <subitem
+ label="Wait for the background task to complete. When the "Review New Imports" dialog appears, click "OK".">
+ </subitem>
+ </item>
+
+
+ <item title="Create The SCA Composites That Describe The Java Resources" skip="true">
+ <description>
+ Create a new HelloWorld service composite that exports the HelloWorldImpl class using the HelloWorld interface.
+ Then create a new HelloWorld cli composite that imports the HelloWorld interface and exports a ConsoleCommandHandler
+ service to allow user interaction via the command handler.
+ </description>
+
+ <subitem label="Create the HelloWorld Service" skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+ param1="org.example.helloworld"
+ param2="sca"
+ param3="org.cauldron.sigil.help"
+ param4="archive/sca/org.example.helloworld.service.composite"
+ param5="org.cauldron.sigil.editors.SCACompositeEditor"
+ translate="" />
+ </subitem>
+ <subitem label="Create the HelloWorld Command Line" skip="true">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.CopyResourceFromPlugin"
+ param1="org.example.helloworld"
+ param2="sca"
+ param3="org.cauldron.sigil.help"
+ param4="archive/sca/org.example.helloworld.cli.composite"
+ param5="org.cauldron.sigil.editors.SCACompositeEditor"
+ translate="" />
+ </subitem>
+ </item>
+
+ <item
+ title="Add The SCA Composites To The HelloWorld Bundle">
+ <description>
+ In order to load a composite in a Newton container it needs to be associated with an OSGi bundle that will act as the classpath for that bundle.
+ </description>
+ <subitem
+ label="Open the "org.example.helloworld" project editor by double-clicking on the "sigil.properties" file in the project directory.">
+ </subitem>
+ <subitem
+ label="Select the "Exports" tab.">
+ </subitem>
+ <subitem
+ label="Click on the "Add" button in the Composites section.">
+ </subitem>
+ <subitem
+ label="Add the "org.example.helloworld.service" and "org.example.helloworld.cli" composites to the OSGi bundle.">
+ </subitem>
+ <subitem
+ label="Save the "org.example.helloworld" project editor.">
+ </subitem>
+ </item>
+
+ <item
+ title="Launch A Newton Container">
+ <description>
+ Launch a Newton enabled JVM in which to host the SCA composites you have just created.
+ </description>
+ <subitem
+ label="Make sure that the the "org.example.helloworld" project editor is open.">
+ </subitem>
+ <subitem
+ label="Select the "Overview" tab.">
+ </subitem>
+ <subitem
+ label="Click on the "Launch a newton container" link in the "Testing" section.">
+ </subitem>
+ <subitem
+ label="Wait for the newton container to finish launching. This is signified by the console message: "Boot complete"">
+ </subitem>
+ </item>
+
+ <item
+ title="Install The HelloWorld Composites In The Newton Container">
+ <description>
+ We will now install the two composites in the Newton container we just launched using the short-cut menu items in the eclipse navigation bar.
+ </description>
+ <subitem
+ label="Open the "org.example.helloworld.service.composite" document using the Sigil "SCA Composite Editor".">
+ </subitem>
+ <subitem
+ label="Click on the "Install Composite" short-cut menu item in the navigation bar.">
+ </subitem>
+ <subitem
+ label="Select the "org.example.helloworld" instance and click ok">
+ </subitem>
+ <subitem
+ label="Open the "org.example.helloworld.cli.composite" document using the Sigil "SCA Composite Editor".">
+ </subitem>
+ <subitem
+ label="Again, click on the "Install Composite" short-cut menu item in the navigation bar and install into the "org.example.helloworld" Newton instance.">
+ </subitem>
+ </item>
+
+ <item title="Type "sayhello" on the Newton console" skip="false">
+ <description>
+
+ </description>
+ <subitem
+ label="Select the Newton console.">
+ <action
+ pluginId="org.cauldron.sigil.help"
+ class="org.cauldron.sigil.cheatsheets.actions.ShowViewAction"
+ param1="org.eclipse.ui.console.ConsoleView"
+ translate="" />
+
+ </subitem>
+ <subitem
+ label="Type "sayhello". You should see the response:<br/>Hello World">
+ </subitem>
+ </item>
+
+ <onCompletion>Congratulations you have created your first composite application using Sigil.</onCompletion>
+
+
+</cheatsheet>
diff --git a/sigil/org.cauldron.sigil.help/html/book.html b/sigil/org.cauldron.sigil.help/html/book.html
new file mode 100644
index 0000000..a9fc420
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/book.html
@@ -0,0 +1,45 @@
+<html>
+<header>
+<title>Sigil - Newton Development Plugin</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Sigil - Newton Development Plugin</h1></td><td align="right"><img src="images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<p>Sigil is an eclipse plugin that helps developers build and test code to be deployed on the Newton platform</p>
+
+<p>Features:<br/>
+<ul>
+ <li>New (Newton Project, Composite, System)</li>
+ <li>Syntax highlighting and code completion for SCA/System docs</li>
+ <li>Launch Newton in Debug/Standard vm from Eclipse</li>
+ <li>Full debug support - break points/source code/step through etc</li>
+ <li>Install Composites into running newton from Eclipse</li>
+ <li>Log integration</li>
+</ul>
+</p>
+
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/container_log.html b/sigil/org.cauldron.sigil.help/html/tasks/container_log.html
new file mode 100644
index 0000000..9b8808f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/container_log.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Viewing The Newton Log</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Viewing The Newton Log</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html
new file mode 100644
index 0000000..43db6da
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/debug_composite.html
@@ -0,0 +1,66 @@
+<html>
+<header>
+<title>Debugging A Composite</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Debugging A Composite</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<p>In order to debug composite applications using the Newton Eclipse
+ plugin, use the runtime configuration you created in above, and start it
+ using a debug session. This will launch a newton container as a separate
+ process to the Eclipse IDE environment but which can be controlled from
+ the IDE</p>
+
+
+<div align="center">
+<img class="figure" alt="Debug" src="my-images/debug.png"></div>
+
+<p>Install your composite into this container using the Newton context
+ menu in the package view as documented above. In this example we have
+ installed the gui.composite component from the fractal render demo into
+ a Newton debug container.</p>
+
+
+<div align="center">
+<img class="figure" alt="Fractal GUI Debug" src="my-images/demo-gui.png"></div>
+
+<p>You can now add break points within your code to allow you to get a
+ better idea of what is going on within your application. In this example
+ we have added a break point to the FractalViewFrame of the fractal
+ render demo to break out into an interactive debug session when any of
+ the buttons <em>draw</em>, <em>save</em>, <em>cancel</em> are pressed on
+ the gui window.</p>
+
+
+<div align="center">
+<img class="figure" alt="Add a breakpoint" src="my-images/breakpoint.png"></div>
+
+<p>You can then inspect variable values in top-right-hand corner as per
+ normal debugging in the Eclipse IDE.</p>
+
+<div align="center">
+<img class="figure" alt="Debug breakpoint" src="my-images/debug-breakpoint.png"></div>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html
new file mode 100644
index 0000000..e20d472
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/install_composite.html
@@ -0,0 +1,41 @@
+<html>
+<header>
+<title>Installing A Composite</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Installing A Composite</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<p>To install a composite into a Infiniflow Service Fabric container, you require a running
+ container instance on your machine. This can either be an external
+ process which you have launched using the bin/container script or the
+ container can be launched via an Eclipse runtime configuration as per
+ the instructions <a href="launch_newton.html" alt="Launching Newton">here</a>.</p>
+
+<p>You can then select your .composite file in the
+ package view and select Newton->Install Composite using the context
+ menu.</p>
+
+
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2008 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html b/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html
new file mode 100644
index 0000000..6a2edc4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/launch_newton.html
@@ -0,0 +1,55 @@
+<html>
+<header>
+<title>Launching Newton</title>
+</header>
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Launching Newton</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+ <h2>Runtime Configuration</h2>
+ <p>To run a Infiniflow Service Fabric container within Eclipse you first need to configure
+ its runtime options in the IDE.</p>
+
+ <p>Open the Run configuration dialog by selecting the Run → Run… option
+ from the menu bar.</p>
+
+ <div align="center"><img class="figure" alt="Run Menu" src="my-images/run-menu.png"></div>
+
+ <p>Create a new Newton configuration by right-clicking on the Newton
+ configuration group and selecting <em>New</em>. Type in a name for your
+ configuration, the other default options should be sufficient for most
+ users.</p>
+
+
+ <div align="center"><img class="figure" alt="Run Dialog" src="my-images/run.png"></div>
+
+ <h2>Launch Newton</h2>
+ <p>Click the <em>Run</em> button to start the container, you will see
+ the container output appear in the console tab. Wait until the <em>Boot
+ Complete</em> line appears, as below.</p>
+
+
+ <div align="center"><img class="figure" alt="Boot Complete" src="my-images/run-complete.png"></div>
+
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html b/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html
new file mode 100644
index 0000000..58d9541
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_composite.html
@@ -0,0 +1,41 @@
+<html>
+<header>
+<title>Creating SCA Composites</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Creating SCA Composites</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+ <p>
+ Create a new sca composite document using the <em>New->Other</em> menu option.<br/>
+ Page 1:<br/>
+ <img src="../images/new_composite_page1.png" alt="New Composite Page 1" /><br/>
+ Page 2:<br/>
+ <img src="../images/new_composite_page2.png" alt="New Composite Page 2" />
+ </p>
+ <p>There is basic support for context sensitive text completion in sca documents. Press <em>Ctrl+Space</em> to see a list of valid elements at the current cursor location.<br/>
+ <img src="../images/composite_suggest_text.png" alt="New Composite Page 2" />
+ </p>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_project.html b/sigil/org.cauldron.sigil.help/html/tasks/new_project.html
new file mode 100644
index 0000000..90e14e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_project.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Creating A Newton Project</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Creating A Newton Project</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/new_system.html b/sigil/org.cauldron.sigil.help/html/tasks/new_system.html
new file mode 100644
index 0000000..a0cc7ae
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/new_system.html
@@ -0,0 +1,31 @@
+<html>
+<header>
+<title>Creating Newton Systems</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Creating Newton Systems</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</td></tr></table>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html b/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html
new file mode 100644
index 0000000..8334f70
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/html/tasks/sigil_config.html
@@ -0,0 +1,32 @@
+<html>
+<header>
+<title>Configuring Sigil</title>
+</header>
+<body bgcolor="#ffffff">
+<table width="100%">
+ <tr>
+ <td valign="bottom"><h1>Configuring Sigil</h1></td><td align="right"><img src="../images/sigil.gif" /></td>
+ </tr>
+ <tr><td colspan="2">
+<p>After having installed sigil, the preferences dialog should appear and ask you to specify the location of a valid newton install directory.</p>
+<div id="footer">
+<table cellpadding="4" cellspacing="0" border="0">
+<tr>
+<td><a href="http://www.paremus.com/License">
+ Copyright © 2003-2007 Paremus Limited.</a>
+ All rights reserved.
+ <br>
+
+<script type="text/javascript" language="JavaScript"><!--
+ document.write(" - "+"Last Published: " + document.lastModified);
+ // --></script>
+<div id="feedback">
+ Send feedback about the website to:
+ <a id="feedbackto" href="mailto:webmaster@paremus.com?subject=Feedback%C2%A0common/eclipse/UsingEclipsePlugin.html">webmaster@paremus.com</a>
+</div>
+</td><td nowrap class="footerLogos"></td>
+</tr>
+</table>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/plugin.xml b/sigil/org.cauldron.sigil.help/plugin.xml
new file mode 100644
index 0000000..0586903
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/plugin.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.cheatsheets.cheatSheetContent">
+ <category
+ id="org.cauldron.sigil.help.category"
+ name="Sigil">
+ </category>
+ <cheatsheet
+ category="org.cauldron.sigil.help.category"
+ composite="false"
+ contentFile="cheatsheets/HelloWorld.xml"
+ id="org.cauldron.sigil.help.createProjectCheatSheet"
+ name="Create a Hello World composite application">
+ </cheatsheet>
+ </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java
new file mode 100644
index 0000000..5875264
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/AbstractNewWizardAction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+public abstract class AbstractNewWizardAction extends Action {
+
+ @Override
+ public void run() {
+ Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+ try {
+ INewWizard wizard = createWizard();
+ wizard.init(PlatformUI.getWorkbench(), getSelection());
+ WizardDialog dialog = new WizardDialog(shell, wizard);
+ int res = dialog.open();
+ notifyResult(res == Window.OK);
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ protected abstract INewWizard createWizard() throws CoreException;
+
+ private IStructuredSelection getSelection() {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window != null)
+ {
+ ISelection selection = window.getSelectionService().getSelection();
+ if (selection instanceof IStructuredSelection)
+ {
+ return (IStructuredSelection)selection;
+ }
+ }
+ return StructuredSelection.EMPTY;
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java
new file mode 100644
index 0000000..81cdccd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/CopyResourceFromPlugin.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+import org.eclipse.ui.part.FileEditorInput;
+import org.osgi.framework.Bundle;
+
+public class CopyResourceFromPlugin extends Action implements ICheatSheetAction {
+
+ private String targetProject;
+ private String targetFolder;
+ private String sourceBundle;
+ private String sourcePath;
+ private String editorID;
+
+ public void run(String[] params, ICheatSheetManager manager) {
+ if ( params != null && params.length > 4 ) {
+ targetProject = params[0];
+ targetFolder = params[1];
+ sourceBundle= params[2];
+ sourcePath = params[3];
+ editorID = params[4];
+ }
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException {
+ try {
+ Bundle b = Platform.getBundle(sourceBundle);
+
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ IProject project = workspaceRoot.getProject(targetProject);
+ IPath path = new Path( targetFolder ).append( sourcePath.substring( sourcePath.lastIndexOf( '/' ) ) );
+ IFile file = project.getFile( path );
+
+ if ( !file.exists() ) {
+ mkdirs( (IFolder) file.getParent(), monitor );
+
+ InputStream in = FileLocator.openStream(b, new Path(sourcePath), false);
+ file.create(in, true, monitor);
+ }
+
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ FileEditorInput input = new FileEditorInput(file);
+ window.getActivePage().openEditor(input, editorID);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ };
+
+ try {
+ new ProgressMonitorDialog(Display.getCurrent().getActiveShell()).run(false, false, op);
+ } catch (InvocationTargetException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void mkdirs(IFolder folder, IProgressMonitor monitor) throws CoreException {
+ IContainer parent = folder.getParent();
+ if ( !parent.exists() ) {
+ mkdirs((IFolder) parent, monitor);
+ }
+
+ if ( !folder.exists() ) {
+ folder.create(true, true, monitor);
+ }
+
+ }
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java
new file mode 100644
index 0000000..34d08da
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/OpenEmptySigilProjectWizardAction.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.ui.wizard.project.SigilProjectWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class OpenEmptySigilProjectWizardAction extends AbstractNewWizardAction implements ICheatSheetAction {
+
+ private String name;
+
+ public void run(String[] params, ICheatSheetManager manager) {
+ if ( params != null && params.length > 0 ) {
+ name = params[0];
+ }
+
+ run();
+ }
+
+ @Override
+ protected INewWizard createWizard() throws CoreException {
+ SigilProjectWizard wizard = new SigilProjectWizard();
+ wizard.setName(name);
+ return wizard;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java
new file mode 100644
index 0000000..a851e3d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ResolveProjectDependencies.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.actions.ResolveProjectDependenciesAction;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class ResolveProjectDependencies extends Action implements ICheatSheetAction {
+
+ private String targetProject;
+
+ public void run(String[] params, ICheatSheetManager manager) {
+ if ( params != null && params.length > 3 ) {
+ targetProject = params[0];
+ }
+
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ IProject project = workspaceRoot.getProject(targetProject);
+
+ try {
+ ISigilProjectModel sigil = SigilCore.create(project);
+ new ResolveProjectDependenciesAction(sigil, false).run();
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java
new file mode 100644
index 0000000..ef48890
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/cheatsheets/actions/ShowViewAction.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.cheatsheets.actions;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.jface.action.Action;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.cheatsheets.ICheatSheetAction;
+import org.eclipse.ui.cheatsheets.ICheatSheetManager;
+
+public class ShowViewAction extends Action implements ICheatSheetAction {
+
+ public void run(String[] params, ICheatSheetManager manager) {
+ if ( params != null && params.length > 0 ) {
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IWorkbenchPage page = window.getActivePage();
+ try {
+ page.showView(params[0]);
+ } catch (PartInitException e) {
+ SigilCore.error( "Failed to show view", e);
+ }
+ }
+
+ }
+}
diff --git a/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java
new file mode 100644
index 0000000..8a94528
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/src/org/cauldron/sigil/help/Activator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.help;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.help";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.help/toc.xml b/sigil/org.cauldron.sigil.help/toc.xml
new file mode 100644
index 0000000..191daf4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.help/toc.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<toc label="Sigil - Newton Development Plugin" topic="html/book.html">
+ <topic label="Configuring Sigil" href="html/tasks/sigil_config.html"/>
+ <topic label="Creating A Newton Project" href="html/tasks/new_project.html"/>
+ <topic label="Creating SCA Composites" href="html/tasks/new_composite.html"/>
+ <topic label="Creating Newton Systems" href="html/tasks/new_system.html"/>
+ <topic label="Launching Newton" href="html/tasks/launch_newton.html"/>
+ <topic label="Viewing The Newton Log" href="html/tasks/container_log.html"/>
+ <topic label="Installing Your Composites" href="html/tasks/install_composite.html"/>
+ <topic label="Debugging Your Composites" href="html/tasks/debug_composite.html"/>
+</toc>
diff --git a/sigil/org.cauldron.sigil.junit/.classpath b/sigil/org.cauldron.sigil.junit/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.junit/.project b/sigil/org.cauldron.sigil.junit/.project
new file mode 100644
index 0000000..30c4b02
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.junit</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1108409
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Junit Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.junit;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.junit.activator.Activator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.cauldron.sigil.core;bundle-version="0.7.0",
+ org.apache.ant;bundle-version="1.7.0",
+ org.junit4;bundle-version="4.3.1",
+ org.junit4.source;bundle-version="4.3.1";resolution:=optional
diff --git a/sigil/org.cauldron.sigil.junit/build.properties b/sigil/org.cauldron.sigil.junit/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/sigil/org.cauldron.sigil.junit/plugin.xml b/sigil/org.cauldron.sigil.junit/plugin.xml
new file mode 100644
index 0000000..99f4abc
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/plugin.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java b/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java
new file mode 100644
index 0000000..b9bbc32
--- /dev/null
+++ b/sigil/org.cauldron.sigil.junit/src/org/cauldron/sigil/junit/activator/Activator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.junit.activator;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.junit";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ public static void runTests(ISigilProjectModel project) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.obr.feature/.project b/sigil/org.cauldron.sigil.obr.feature/.project
new file mode 100644
index 0000000..cc440e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.obr.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.obr.feature/build.properties b/sigil/org.cauldron.sigil.obr.feature/build.properties
new file mode 100644
index 0000000..64f93a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/sigil/org.cauldron.sigil.obr.feature/feature.xml b/sigil/org.cauldron.sigil.obr.feature/feature.xml
new file mode 100644
index 0000000..c7d8f45
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr.feature/feature.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<feature
+ id="org.cauldron.sigil.obr.feature"
+ label="Sigil OBR"
+ version="0.8.0.qualifier"
+ provider-name="Paremus Ltd">
+
+ <description>
+ Sigil OBR plugin, allows Sigil to use OSGi Bundle Repositories to download OSGi bundles into the Eclipse workspace.
+ </description>
+
+ <copyright>
+ Copyright (C) 2003-2008 Paremus Limited, London, United Kingdom
+ </copyright>
+
+ <license url="http://www.apache.org/licenses/LICENSE-2.0">
+ Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+or implied.
+See the License for the specific language governing permissions
+and
+limitations under the License.
+ </license>
+
+ <url>
+ <update url="http://replace.with.real.url"/>
+ </url>
+
+ <requires>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.cauldron.sigil.core" version="0.6.0" match="greaterOrEqual"/>
+ <import plugin="org.cauldron.sigil.ui" version="0.6.0" match="greaterOrEqual"/>
+ <import plugin="org.cauldron.bld.core" version="0.6.0" match="greaterOrEqual"/>
+ <import plugin="org.eclipse.osgi"/>
+ <import plugin="org.eclipse.equinox.common" version="3.4.0" match="greaterOrEqual"/>
+ </requires>
+
+ <plugin
+ id="org.cauldron.sigil.obr"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.cauldron.bld.obr"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/sigil/org.cauldron.sigil.obr/.classpath b/sigil/org.cauldron.sigil.obr/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.obr/.project b/sigil/org.cauldron.sigil.obr/.project
new file mode 100644
index 0000000..7990e04
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.obr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..902de9d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Wed Aug 27 13:25:05 BST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..9a09325
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/META-INF/MANIFEST.MF
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: OBR Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.obr;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.obr.Activator
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.cauldron.sigil.core;bundle-version="0.6.0",
+ org.cauldron.sigil.ui;bundle-version="0.6.0",
+ org.cauldron.bld.core
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-ActivationPolicy: lazy
+Import-Package: org.cauldron.bld.obr,
+ org.cauldron.sigil.model
diff --git a/sigil/org.cauldron.sigil.obr/build.properties b/sigil/org.cauldron.sigil.obr/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/sigil/org.cauldron.sigil.obr/plugin.xml b/sigil/org.cauldron.sigil.obr/plugin.xml
new file mode 100644
index 0000000..03b79c1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/plugin.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.cauldron.sigil.repositoryprovider">
+ <provider
+ class="org.cauldron.bld.obr.OBRRepositoryProvider"
+ dynamic="true"
+ id="org.cauldron.sigil.obr.provider"
+ type="OSGi Bundle Repository (OBR)">
+ </provider>
+ </extension>
+ <extension
+ point="org.cauldron.sigil.ui.repositorywizard">
+ <wizard
+ class="org.cauldron.sigil.obr.OBRRepositoryWizard"
+ repository="org.cauldron.sigil.obr.provider">
+ </wizard>
+ </extension>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java
new file mode 100644
index 0000000..4bad94b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/Activator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.obr";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java
new file mode 100644
index 0000000..79e0e72
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizard.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class OBRRepositoryWizard extends RepositoryWizard {
+
+ @Override
+ public void addPages() {
+ addPage( new OBRRepositoryWizardPage(this) );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java
new file mode 100644
index 0000000..a607ac1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.obr/src/org/cauldron/sigil/obr/OBRRepositoryWizardPage.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.obr;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+
+public class OBRRepositoryWizardPage extends RepositoryWizardPage implements IWizardPage {
+
+ private StringFieldEditor urlEditor;
+ private StringFieldEditor cacheEditor;
+
+ protected OBRRepositoryWizardPage(RepositoryWizard parent) {
+ super("OSGi Bundle Repository", parent);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ createField( urlEditor = new StringFieldEditor("url", "URL:", getFieldEditorParent()) );
+ createField( cacheEditor = new DirectoryFieldEditor("cache", "Cache:", getFieldEditorParent()) );
+ addField( new BooleanFieldEditor( "inmemory", "In Memory:", getFieldEditorParent() ));
+ }
+
+ private void createField(StringFieldEditor editor) {
+ editor.getTextControl(getFieldEditorParent()).addModifyListener( new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ checkPageComplete();
+ }
+ });
+ addField(editor);
+ }
+
+ @Override
+ protected void checkPageComplete() {
+ super.checkPageComplete();
+ if ( isPageComplete() && checkURLComplete() ) {
+ checkCacheComplete();
+ }
+ }
+
+ private boolean checkCacheComplete() {
+ setPageComplete(cacheEditor.getStringValue().length() > 0);
+
+ if ( isPageComplete() ) {
+ if ( new File( cacheEditor.getStringValue() ).isDirectory() ) {
+ setErrorMessage(null);
+ }
+ else {
+ setErrorMessage("Invalid cache directory");
+ setPageComplete(false);
+ }
+ }
+
+ return isPageComplete();
+ }
+
+ private boolean checkURLComplete() {
+ setPageComplete(urlEditor.getStringValue().length() > 0);
+
+ if ( isPageComplete() ) {
+ try {
+ new URL(urlEditor.getStringValue());
+ setErrorMessage(null);
+ }
+ catch (MalformedURLException e) {
+ if ( !new File(urlEditor.getStringValue()).isFile() ) {
+ setErrorMessage("Invalid repository url: " + e.getMessage());
+ setPageComplete(false);
+ }
+ }
+ }
+
+ return isPageComplete();
+ }
+
+ @Override
+ public void storeFields() {
+ super.storeFields();
+ IPath dir = Activator.getDefault().getStateLocation();
+ getModel().getPreferences().setValue( "index", dir.append( getModel().getId() + ".obr" ).toOSString() );
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.search/.classpath b/sigil/org.cauldron.sigil.search/.classpath
new file mode 100644
index 0000000..254ffb7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry exported="true" kind="lib" path="lib/bcel-5.2.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.search/.project b/sigil/org.cauldron.sigil.search/.project
new file mode 100644
index 0000000..3e37565
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.search</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..636afaf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Fri Oct 03 18:13:19 PDT 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7a97ca1
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Search Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.search;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.search.SigilSearch
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.jdt.core;bundle-version="3.4.0",
+ org.cauldron.sigil.core;bundle-version="0.7.0",
+ org.cauldron.bld.core;bundle-version="0.7.0",
+ org.eclipse.core.resources;bundle-version="3.4.0"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.cauldron.sigil.search
+Bundle-ClassPath: lib/bcel-5.2.jar,
+ .
diff --git a/sigil/org.cauldron.sigil.search/build.properties b/sigil/org.cauldron.sigil.search/build.properties
new file mode 100644
index 0000000..ef5e456
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ lib/bcel-5.2.jar
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java
new file mode 100644
index 0000000..f47deed
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/ISearchResult.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+
+public interface ISearchResult {
+ ISigilBundle getProvider();
+ IPackageExport getExport();
+ String getPackageName();
+ String getClassName();
+}
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java
new file mode 100644
index 0000000..79ca623
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/SigilSearch.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Pattern;
+
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.cauldron.sigil.search.index.Index;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilSearch extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.search";
+
+ private static final String CLASS_EXTENSION = ".class";
+
+ // The shared instance
+ private static SigilSearch plugin;
+ private static Index index;
+
+ /**
+ * The constructor
+ */
+ public SigilSearch() {
+ }
+
+ public static List<ISearchResult> findProviders(String fullyQualifiedName, ISigilProjectModel sigil, IProgressMonitor monitor) {
+ listen(sigil);
+ return index.findProviders(fullyQualifiedName, monitor);
+ }
+
+ public static List<ISearchResult> findProviders(Pattern namePattern, ISigilProjectModel sigil, IProgressMonitor monitor) {
+ listen(sigil);
+ return index.findProviders(namePattern, monitor);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static SigilSearch getDefault() {
+ return plugin;
+ }
+
+ private static void listen(ISigilProjectModel sigil) {
+ synchronized(plugin) {
+ if ( index == null ) {
+ index = new Index();
+ for ( IBundleRepository rep : SigilCore.getRepositoryManager(sigil).getRepositories() ) {
+ index(index, rep);
+ }
+
+ SigilCore.getRepositoryManager(sigil).addRepositoryChangeListener( new IRepositoryChangeListener() {
+ public void repositoryChanged(RepositoryChangeEvent event) {
+ index(index, event.getRepository());
+ }
+ });
+ }
+ }
+ }
+
+ private static void index(final Index index, final IBundleRepository rep) {
+ index.delete(rep);
+ rep.accept( new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class);
+ if ( p == null ) {
+ if ( bundle.isSynchronized() ) {
+ IPath loc = bundle.getLocation();
+ if ( loc.isAbsolute() ) {
+ indexJar(rep, bundle, loc);
+ }
+ }
+ }
+ else {
+ indexProject(rep, p);
+ }
+ return true;
+ }
+ });
+ }
+
+ private static void indexProject(IBundleRepository rep, ISigilProjectModel sigil) {
+ try {
+ for (ICompilationUnit unit : JavaHelper.findCompilationUnits(sigil) ) {
+ IPackageFragment p = (IPackageFragment) unit.getParent();
+ ISigilBundle b = sigil.getBundle();
+ IPackageExport export = b.findExport(p.getElementName());
+ index.addEntry(unit, rep, b, export != null);
+ }
+ } catch (JavaModelException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private static void indexJar(IBundleRepository rep, ISigilBundle bundle, IPath loc) {
+ JarFile jar = null;
+ try {
+ jar = new JarFile(loc.toOSString());
+ for (Map.Entry<JarEntry, IPackageExport> export : findExportedClasses(bundle, jar).entrySet() ) {
+ JarEntry entry = export.getKey();
+ InputStream in = null;
+ try {
+ in = jar.getInputStream(entry);
+ ClassParser parser = new ClassParser(in, entry.getName());
+ JavaClass c = parser.parse();
+ index.addEntry(c, rep, bundle, true);
+ }
+ finally {
+ if ( in != null ) {
+ in.close();
+ }
+ }
+ }
+ }
+ catch (IOException e) {
+ SigilCore.error( "Failed to read jar " + loc, e );
+ }
+ finally {
+ if ( jar != null ) {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ SigilCore.error( "Failed to close jar " + loc, e );
+ }
+ }
+ }
+ }
+
+ private static Map<JarEntry, IPackageExport> findExportedClasses(ISigilBundle bundle, JarFile jar) {
+ HashMap<JarEntry, IPackageExport> found = new HashMap<JarEntry, IPackageExport>();
+
+ IPackageExport[] exports = bundle.getBundleInfo().childrenOfType(IPackageExport.class);
+ if ( exports.length > 0 ) {
+ Arrays.sort(exports, new Comparator<IPackageExport> () {
+ public int compare(IPackageExport o1, IPackageExport o2) {
+ return -1 * o1.compareTo(o2);
+ }
+ });
+ for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements();) {
+ JarEntry entry = e.nextElement();
+ String className = toClassName(entry);
+ if ( className != null ) {
+ IPackageExport ex = findExport(className, exports);
+
+ if ( found != null ) {
+ found.put( entry, ex );
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ private static IPackageExport findExport(String className, IPackageExport[] exports) {
+ for ( IPackageExport e : exports ) {
+ if ( className.startsWith(e.getPackageName())) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ private static String toClassName(JarEntry entry) {
+ String name = entry.getName();
+ if ( name.endsWith(CLASS_EXTENSION) ) {
+ name = name.substring(0, name.length() - CLASS_EXTENSION.length());
+ name = name.replace('/', '.');
+ return name;
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java
new file mode 100644
index 0000000..b62421f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.search/src/org/cauldron/sigil/search/index/Index.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.search.index;
+
+import java.lang.ref.SoftReference;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Pattern;
+
+import org.apache.bcel.classfile.JavaClass;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.search.ISearchResult;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.osgi.framework.Version;
+
+public class Index {
+ private HashMap<String, ClassData> primary = new HashMap<String, ClassData>();
+ private HashMap<IBundleRepository, HashSet<String>> secondary = new HashMap<IBundleRepository, HashSet<String>>();
+
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ static class ClassData {
+ HashMap<IBundleRepository, Set<ISearchResult>> provided = new HashMap<IBundleRepository, Set<ISearchResult>>();
+
+ void add(IBundleRepository rep, ISearchResult export) {
+ Set<ISearchResult> exports = provided.get(rep);
+
+ if ( exports == null ) {
+ exports = new HashSet<ISearchResult>();
+ provided.put( rep, exports );
+ }
+
+ exports.add(export);
+ }
+
+ List<ISearchResult> getResults() {
+ LinkedList<ISearchResult> exports = new LinkedList<ISearchResult>();
+ for ( Set<ISearchResult> p : provided.values() ) {
+ exports.addAll(p);
+ }
+ return exports;
+ }
+
+ void remove(IBundleRepository rep) {
+ provided.remove(rep);
+ }
+
+ boolean isEmpty() {
+ return provided.isEmpty();
+ }
+ }
+
+ static class SearchResult implements ISearchResult {
+ private final String className;
+ private final String packageName;
+ private final IBundleRepository rep;
+ private final String bundleSymbolicName;
+ private final Version version;
+ private final boolean exported;
+
+ private SoftReference<ISigilBundle> bundleReference;
+ private SoftReference<IPackageExport> exportReference;
+
+ public SearchResult(String className, IBundleRepository rep, ISigilBundle bundle, String packageName, boolean exported) {
+ this.className = className;
+ this.rep = rep;
+ this.exported = exported;
+ this.bundleSymbolicName = bundle.getBundleInfo().getSymbolicName();
+ this.version = bundle.getVersion();
+ this.packageName = packageName;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public IPackageExport getExport() {
+ IPackageExport ipe = null;
+ if ( exported ) {
+ ipe = exportReference == null ? null : exportReference.get();
+ if (ipe == null) {
+ ipe = getProvider().findExport(packageName);
+ exportReference = new SoftReference<IPackageExport>(ipe);
+ }
+ }
+ return ipe;
+ }
+
+ public ISigilBundle getProvider() {
+ ISigilBundle b = bundleReference == null ? null : bundleReference.get();
+ if ( b == null ) {
+ IRequiredBundle rb = ModelElementFactory.getInstance().newModelElement(IRequiredBundle.class);
+ rb.setSymbolicName(bundleSymbolicName);
+ VersionRange versions = new VersionRange(false, version, version, false);
+ rb.setVersions(versions);
+ b = rep.findProvider(rb, 0);
+ bundleReference = new SoftReference<ISigilBundle>(b);
+ }
+ return b;
+ }
+
+ }
+
+ public void addEntry(JavaClass c, IBundleRepository rep, ISigilBundle bundle, boolean exported) {
+ addEntry(c.getClassName(), rep, bundle, c.getPackageName(), exported);
+ }
+
+ public void addEntry(ICompilationUnit unit, IBundleRepository rep, ISigilBundle bundle, boolean exported) {
+ String name = unit.getElementName();
+ if ( name.endsWith( ".java" ) ) {
+ name = name.substring(0, name.length() - 5 );
+ }
+ IPackageFragment p = (IPackageFragment) unit.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
+ addEntry(p.getElementName() + "." + name, rep, bundle, p.getElementName(), exported);
+ }
+
+ private void addEntry(String className, IBundleRepository rep, ISigilBundle bundle, String packageName, boolean exported) {
+ List<String> keys = genKeys(className);
+ lock.writeLock().lock();
+ try {
+ for ( String key : keys ) {
+ ClassData data = primary.get(key);
+
+ if ( data == null ) {
+ data = new ClassData();
+ primary.put(key, data);
+ }
+
+ SearchResult result = new SearchResult(className, rep, bundle, packageName, exported);
+ data.add(rep, result);
+ }
+
+ HashSet<String> all = secondary.get(rep);
+ if ( all == null ) {
+ all = new HashSet<String>();
+ secondary.put(rep, all);
+ }
+ all.addAll(keys);
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+
+ public List<ISearchResult> findProviders(String className, IProgressMonitor monitor) {
+ lock.readLock().lock();
+ try {
+ ClassData data = primary.get(className);
+ return data == null ? Collections.<ISearchResult>emptyList() : data.getResults();
+ }
+ finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public List<ISearchResult> findProviders(Pattern className, IProgressMonitor monitor) {
+ lock.readLock().lock();
+ try {
+ ClassData data = primary.get(className);
+ return data == null ? Collections.<ISearchResult>emptyList() : data.getResults();
+ }
+ finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public void delete(IBundleRepository rep) {
+ lock.writeLock().lock();
+ try {
+ Set<String> keys = secondary.remove(rep);
+ if ( keys != null ) {
+ for ( String key : keys ) {
+ ClassData data = primary.get(key);
+ data.remove(rep);
+ if ( data.isEmpty() ) {
+ primary.remove(key);
+ }
+ }
+ }
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ private List<String> genKeys(String className) {
+ LinkedList<String> keys = new LinkedList<String>();
+ keys.add(className);
+ int i = className.lastIndexOf('.');
+ if ( i != -1 ) {
+ String name = className.substring(i + 1);
+ keys.add( name );
+ }
+ return keys;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/.classpath b/sigil/org.cauldron.sigil.ui/.classpath
new file mode 100644
index 0000000..6f93ad7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="org/eclipse/pde/internal/ui/parts/*"/>
+ </accessrules>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.ui/.project b/sigil/org.cauldron.sigil.ui/.project
new file mode 100644
index 0000000..e736268
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4b4103a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Ui Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.ui;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.ui.SigilUI
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.cauldron.sigil.core,
+ org.cauldron.bld.core,
+ org.cauldron.sigil.utils,
+ org.cauldron.sigil.search,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.pde.ui,
+ org.eclipse.ui,
+ org.eclipse.ui.editors,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.help,
+ org.eclipse.zest.core;bundle-version="[1,2)",
+ org.eclipse.zest.layouts;bundle-version="[1,2)",
+ org.eclipse.ltk.core.refactoring
+Eclipse-LazyStart: true
+Export-Package: org.cauldron.sigil.actions,
+ org.cauldron.sigil.handlers,
+ org.cauldron.sigil.ui,
+ org.cauldron.sigil.ui.util,
+ org.cauldron.sigil.ui.wizard.project,
+ org.cauldron.sigil.ui.wizard.repository
+Bundle-Localization: plugin
+
diff --git a/sigil/org.cauldron.sigil.ui/build.properties b/sigil/org.cauldron.sigil.ui/build.properties
new file mode 100644
index 0000000..df5bef4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/build.properties
@@ -0,0 +1,12 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ etc/,\
+ plugin.xml,\
+ icons/,\
+ resources/,\
+ toc.xml,\
+ html/,\
+ plugin.properties,\
+ schema/
diff --git a/sigil/org.cauldron.sigil.ui/plugin.properties b/sigil/org.cauldron.sigil.ui/plugin.properties
new file mode 100644
index 0000000..3e6d20d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.properties
@@ -0,0 +1,5 @@
+repositoriesPrefPage=Repositories
+newtonRepoPrefs=Newton Repository
+librariesPrefsPage=OSGi Libraries
+commandConvertProject=Convert Project To Sigil Project
+commandRefreshClasspath=Refresh bundle classpath
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/plugin.xml b/sigil/org.cauldron.sigil.ui/plugin.xml
new file mode 100644
index 0000000..d8b96a5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard" schema="schema/org.cauldron.sigil.ui.repositorywizard.exsd"/>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup class="org.cauldron.sigil.startup.SigilStartup"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <category
+ id="org.cauldron.sigil.newWizardCategory"
+ name="Sigil"/>
+ <wizard
+ category="org.cauldron.sigil.newWizardCategory"
+ class="org.cauldron.sigil.ui.wizard.project.SigilProjectWizard"
+ finalPerspective="org.cauldron.sigil.ui.perspective"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.editors.newProjectWizard"
+ name="Sigil Project"
+ project="true"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ class="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ name="Sigil"/>
+ <!--page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.LibraryPreferencePage"
+ id="org.cauldron.sigil.ui.libraryPreferences"
+ name="%librariesPrefsPage" >
+ </page-->
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.repository.RepositoriesPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.repositoriesPreferencePage"
+ name="%repositoriesPrefPage">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.ExcludedResourcesPrefsPage"
+ id="org.cauldron.sigil.ui.preferences.excludedResources"
+ name="Excluded Resources">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+ name="Version Handling">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.installs.OSGiInstallsPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.newtonInstalls"
+ name="Newton Installs">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.editors">
+ <editor
+ class="org.cauldron.sigil.ui.editors.project.SigilProjectEditorPart"
+ default="true"
+ filenames="sigil.properties"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.editors.SigilProjectEditor"
+ name="Sigil Project Editor"/>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.core.classpathContainerInitializer">
+ <classpathContainerInitializer
+ class="org.cauldron.sigil.classpath.SigilClasspathContainerInitializer"
+ id="org.cauldron.sigil.core.classpathContainer"/>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.ui.classpathContainerPage">
+ <classpathContainerPage
+ class="org.cauldron.sigil.classpath.SigilLibraryPage" id="org.cauldron.sigil.core.classpathContainer"
+ name="Sigil Library"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="org.cauldron.sigil.ui.perspective.SigilPerspectiveFactory"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.ui.perspective"
+ name="Sigil">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.ui.quickFixProcessors">
+ <quickFixProcessor
+ class="org.cauldron.sigil.ui.quickfix.ImportQuickFixProcessor"
+ id="org.cauldron.sigil.ui.quickFixProcessor1">
+ </quickFixProcessor>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ allowMultiple="false"
+ category="org.cauldron.sigil.ui.views"
+ class="org.cauldron.sigil.ui.views.resolution.BundleResolverView"
+ icon="icons/jar_web_obj.png"
+ id="org.cauldron.sigil.ui.bundleDependencyView"
+ name="Bundle Dependency View"
+ restorable="true">
+ </view>
+ <view
+ allowMultiple="false"
+ category="org.cauldron.sigil.ui.views"
+ class="org.cauldron.sigil.ui.views.RepositoryViewPart"
+ icon="icons/jars_obj.png"
+ id="org.cauldron.sigil.ui.repositoryBrowser"
+ name="Repository Browser"
+ restorable="true">
+ </view>
+ <category
+ id="org.cauldron.sigil.ui.views"
+ name="Sigil">
+ </category>
+ </extension>
+ <extension
+ point="org.cauldron.sigil.ui.repositorywizard">
+ <wizard
+ class="org.cauldron.sigil.ui.internal.repository.FileSystemRepositoryWizard"
+ repository="org.cauldron.sigil.core.file">
+ </wizard>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="org.cauldron.sigil.ui.commands.renameComposite"
+ name="Rename Composite">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ltk.core.refactoring.refactoringContributions">
+ <contribution
+ class="org.cauldron.sigil.ui.refactor.RenameCompositeRefactoring"
+ id="org.cauldron.sigil.ui.rename.composite">
+ </contribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.propertyPages">
+ <page
+ class="org.cauldron.sigil.ui.preferences.project.ProjectPropertyPage"
+ id="org.cauldron.sigil.ui.projectpage"
+ name="Repositories">
+ <filter
+ name="projectNature"
+ value="org.cauldron.sigil.core.newtonnature"/>
+ <enabledWhen>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ </adapt>
+ </enabledWhen>
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:org.eclipse.ui.popup.any">
+ <menu
+ icon="etc/images/newton.png"
+ label="Sigil">
+ <command
+ commandId="org.cauldron.sigil.ui.convertproject"
+ label="Convert Project"
+ style="push">
+ <visibleWhen
+ checkEnabled="true">
+ </visibleWhen>
+ </command>
+ </menu>
+ <separator
+ name="org.cauldron.sigil.ui.separator"
+ visible="true">
+ </separator>
+ <command
+ commandId="org.cauldron.sigil.ui.refreshclasspath"
+ icon="icons/refreshBundle.png"
+ label="Refresh bundle classpath"
+ style="push">
+ <visibleWhen
+ checkEnabled="true">
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="org.cauldron.sigil.ui.convertproject"
+ name="%commandConvertProject">
+ </command>
+ <command
+ id="org.cauldron.sigil.ui.refreshclasspath"
+ name="%commandRefreshClasspath">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commandImages">
+ <image
+ commandId="org.cauldron.sigil.ui.convertproject"
+ icon="etc/images/newton.png">
+ </image>
+ <image
+ commandId="org.cauldron.sigil.ui.refreshclasspath"
+ icon="etc/images/newton.png">
+ </image>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="org.cauldron.sigil.handlers.project.ConvertProjectHandler"
+ commandId="org.cauldron.sigil.ui.convertproject">
+ <activeWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <instanceof
+ value="org.eclipse.core.resources.IProject">
+ </instanceof>
+ <test
+ property="org.cauldron.sigil.isSigilProject"
+ value="false">
+ </test>
+ </iterate>
+ </with>
+ </activeWhen>
+ </handler>
+ <handler
+ class="org.cauldron.sigil.handlers.project.RefreshSigilClasspathHandler"
+ commandId="org.cauldron.sigil.ui.refreshclasspath">
+ <activeWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <instanceof
+ value="org.eclipse.core.resources.IProject">
+ </instanceof>
+ <test
+ property="org.cauldron.sigil.isSigilProject"
+ value="true">
+ </test>
+ </iterate>
+ </with>
+ </activeWhen>
+ </handler>
+ </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
diff --git a/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
new file mode 100644
index 0000000..e0ba4a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.ui" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.cauldron.sigil.ui" id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard"/>
+ </appinfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="0" maxOccurs="unbounded">
+ <element ref="wizard"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="wizard">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="org.cauldron.sigil.ui.wizard.repository.RepositoryWizard:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="repository" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
new file mode 100644
index 0000000..852f972
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public abstract class DisplayAction extends Action {
+
+ public DisplayAction() {
+ super();
+ }
+
+ public DisplayAction(String text) {
+ super(text);
+ }
+
+ public DisplayAction(String text, ImageDescriptor image) {
+ super(text, image);
+ }
+
+ public DisplayAction(String text, int style) {
+ super(text, style);
+ }
+
+ protected Display findDisplay() {
+ Display d = Display.getCurrent();
+
+ if ( d == null ) {
+ d = Display.getDefault();
+ }
+
+ return d;
+ }
+
+ protected void runInUI(final Shell shell, final WorkspaceModifyOperation op) {
+ }
+
+ protected void info(final Shell shell, final String msg) {
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(shell, "Information", msg );
+ }
+ } );
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
new file mode 100644
index 0000000..86886bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class PruneProjectDependenciesAction extends DisplayAction {
+
+ private ISigilProjectModel project;
+
+ public PruneProjectDependenciesAction(ISigilProjectModel project) {
+ this.project = project;
+ }
+
+ @Override
+ public void run() {
+ final Shell shell = findDisplay().getActiveShell();
+
+ Job job = new Job("Resolving imports") {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Collection<IModelElement> unused = JavaHelper.findUnusedReferences(project, monitor);
+
+ if ( unused.isEmpty() ) {
+ info( shell, "No unused references found" );
+ }
+ else {
+ final ResourceReviewDialog<IModelElement> dialog = new ResourceReviewDialog<IModelElement>(shell, "Review Unused Imports", unused);
+
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( dialog.open() == Window.OK ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException {
+ for ( IModelElement e : dialog.getResources() ) {
+ if ( !project.getBundle().getBundleInfo().removeChild(e) ) {
+ SigilCore.error( "Failed to remove " + e );
+ }
+ }
+
+ project.save(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, shell);
+ }
+ }
+ });
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.schedule();
+
+ IProgressService p = PlatformUI.getWorkbench().getProgressService();
+ p.showInDialog(shell, job);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
new file mode 100644
index 0000000..39d3abd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshRepositoryAction extends DisplayAction {
+ private final IRepositoryModel[] model;
+
+ public RefreshRepositoryAction(IRepositoryModel... model) {
+ super( "Refresh repository");
+ this.model = model;
+ }
+
+ @Override
+ public void run() {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException, InvocationTargetException,
+ InterruptedException {
+ boolean changed = false;
+
+ for ( IBundleRepository b : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+ for ( IRepositoryModel m : model ) {
+ if ( b.getId().equals( m.getId() ) ) {
+ b.refresh();
+ changed = true;
+ }
+ }
+ }
+
+ if ( changed ) {
+ List<ISigilProjectModel> projects = SigilCore.getRoot().getProjects();
+ SubMonitor sub = SubMonitor.convert(monitor, projects.size() * 10);
+ for ( ISigilProjectModel p : projects ) {
+ p.resetClasspath(sub.newChild(10));
+ }
+ }
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
new file mode 100644
index 0000000..1f1ec1e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class ResolveProjectDependenciesAction extends DisplayAction {
+
+ private ISigilProjectModel project;
+ private boolean review;
+
+ public ResolveProjectDependenciesAction(ISigilProjectModel project, boolean review) {
+ this.project = project;
+ this.review = review;
+ }
+
+ public void run() {
+ final Shell shell = findDisplay().getActiveShell();
+
+ Job job = new Job("Resolving dependencies" ) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("", IProgressMonitor.UNKNOWN);
+
+ List<IPackageImport> imports = JavaHelper.findRequiredImports(project, monitor);
+
+ if ( imports.isEmpty() ) {
+ info( shell, "No new dependencies found" );
+ }
+ else {
+ Collections.sort(imports, new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ int i = o1.getPackageName().compareTo(o2.getPackageName());
+
+ // shouldn't get more than one import for same package
+ // but may as well sort if do...
+ if ( i == 0 ) {
+ i = o1.getVersions().getFloor().compareTo(o2.getVersions().getFloor() );
+ }
+
+ return i;
+ }
+ });
+
+ final ResourceReviewDialog<IPackageImport> dialog = new ResourceReviewDialog<IPackageImport>(shell, "Review New Dependencies", imports);
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( !review || dialog.open() == Window.OK ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException {
+ for ( IPackageImport pi : dialog.getResources() ) {
+ project.getBundle().getBundleInfo().addImport(pi);
+ }
+
+ project.save(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, shell);
+ }
+ }
+ } );
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.schedule();
+
+ IProgressService p = PlatformUI.getWorkbench().getProgressService();
+ p.showInDialog(shell, job);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
new file mode 100644
index 0000000..69d8894
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ThreadProgressMonitor;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilClassPathContainer implements IClasspathContainer {
+
+ private IClasspathEntry[] entries;
+ private ISigilProjectModel sigil;
+
+ public SigilClassPathContainer(ISigilProjectModel sigil) {
+ this.sigil = sigil;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
+ */
+ public IClasspathEntry[] getClasspathEntries() {
+ if ( entries == null ) {
+ buildClassPathEntries();
+ }
+
+ return entries;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
+ */
+ public String getDescription() {
+ return "Bundle Context Classpath";
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getKind()
+ */
+ public int getKind() {
+ return K_SYSTEM;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getPath()
+ */
+ public IPath getPath() {
+ return new Path( SigilCore.CLASSPATH_CONTAINER_PATH );
+ }
+
+ /**
+ * @return
+ * @throws CoreException
+ * @throws CoreException
+ */
+ private void buildClassPathEntries() {
+ try {
+ IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+ entries = sigil.findExternalClasspath(monitor).toArray( new IClasspathEntry[0] );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to build classpath entries", e);
+ }
+ finally {
+ if ( entries == null ) {
+ entries = new IClasspathEntry[] {};
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
new file mode 100644
index 0000000..f0229ee
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.job.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+
+public class SigilClasspathContainerInitializer extends ClasspathContainerInitializer {
+
+ public SigilClasspathContainerInitializer() {
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public boolean canUpdateClasspathContainer(IPath containerPath,
+ IJavaProject project) {
+ return true;
+ }
+
+ @Override
+ public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion)
+ throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project.getProject());
+
+ IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
+
+ IJavaProject[] affectedProjects = new IJavaProject[] { project };
+
+ IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
+
+ IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+
+ if ( monitor == null ) {
+ monitor = Job.getJobManager().createProgressGroup();
+ }
+
+ JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);
+ }
+
+ @Override
+ public void initialize(IPath containerPath, IJavaProject project)
+ throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project.getProject());
+
+ IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
+
+ IJavaProject[] affectedProjects = new IJavaProject[] { project };
+
+ IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
+
+ IProgressMonitor monitor = Job.getJobManager().createProgressGroup();
+
+ JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
new file mode 100644
index 0000000..cd73c8f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.wizards.IClasspathContainerPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class SigilLibraryPage extends WizardPage implements IClasspathContainerPage {
+
+ private IClasspathEntry classpathEntry;
+
+ public SigilLibraryPage() {
+ super( "Sigil" );
+ }
+
+ public void createControl(Composite parent) {
+ setTitle("Sigil Library");
+
+ Label label = new Label(parent, SWT.NONE);
+ label.setText("Press Finish to add the Sigil Library");
+ label.setFont(parent.getFont());
+
+ setControl(label);
+ }
+
+ public boolean finish() {
+ classpathEntry = JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH));
+ return true;
+ }
+
+ public boolean isPageComplete() {
+ return true;
+ }
+
+ public IClasspathEntry getSelection() {
+ return classpathEntry;
+ }
+
+ public void setSelection(IClasspathEntry containerEntry) {
+ this.classpathEntry = containerEntry;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
new file mode 100644
index 0000000..94184a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+
+public abstract class AbstractResourceCommandHandler extends AbstractHandler {
+
+ protected abstract IResourceCommandHandler getResourceCommandHandler();
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
new file mode 100644
index 0000000..8bbcc6f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class EditorResourceCommandHandler extends AbstractResourceCommandHandler {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ final Shell shell = HandlerUtil.getActiveShell(event);
+ final IEditorPart editorPart = HandlerUtil.getActiveEditor(event);
+ if ( editorPart != null ) {
+ IEditorInput editorInput = editorPart.getEditorInput();
+
+ if(!(editorInput instanceof IFileEditorInput)) {
+ throw new ExecutionException("Editor input must be a file");
+ }
+ IFileEditorInput fileInput = (IFileEditorInput) editorInput;
+
+ try {
+ // Save the editor content (if dirty)
+ IRunnableWithProgress saveOperation = new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ if(editorPart.isDirty()) {
+ if(MessageDialog.openQuestion(shell, "Save File", "The file contents must be saved before the command can be executed. Do you wish to save now?")) {
+ editorPart.doSave(monitor);
+ } else {
+ throw new InterruptedException();
+ }
+ }
+ }
+ };
+ new ProgressMonitorDialog(shell).run(false, true, saveOperation);
+
+ // Execute on the file
+ IFile file = fileInput.getFile();
+ getResourceCommandHandler().execute(new IResource[] { file }, event);
+ } catch (InvocationTargetException e) {
+ throw new ExecutionException("Error saving file", e.getTargetException());
+ } catch (InterruptedException e) {
+ // Exit the command silently
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
new file mode 100644
index 0000000..a1eb206
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+
+public interface IResourceCommandHandler {
+
+ Object execute(IResource[] resources, ExecutionEvent event) throws ExecutionException;
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
new file mode 100644
index 0000000..b96cc73
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class SelectionResourceCommandHandler extends AbstractResourceCommandHandler {
+
+ public final Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+
+ Object[] objectArray = ((IStructuredSelection) selection).toArray();
+ IResource[] resourceArray = new IResource[objectArray.length];
+ System.arraycopy(objectArray, 0, resourceArray, 0, objectArray.length);
+
+ return getResourceCommandHandler().execute(resourceArray, event);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
new file mode 100644
index 0000000..8e93c4e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ConvertProjectCommandHandler implements IResourceCommandHandler {
+
+ public Object execute(IResource[] resources, ExecutionEvent event)
+ throws ExecutionException {
+ for ( IResource r : resources ) {
+ final IProject project = (IProject) r;
+ if ( project != null ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ SigilCore.makeSigilProject(project, monitor);
+ IJavaProject java = JavaCore.create(project);
+ ISigilProjectModel sigil = SigilCore.create(project);
+ IClasspathEntry[] entries = java.getRawClasspath();
+ for (int i = 0; i < entries.length; i++) {
+ IClasspathEntry entry = entries[i];
+ if(entry.getEntryKind()==IClasspathEntry.CPE_SOURCE) {
+ String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry( entry );
+ sigil.getBundle().addClasspathEntry(encodedClasspath);
+ }
+ }
+ sigil.save(monitor);
+ }
+ };
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
new file mode 100644
index 0000000..bf012f8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class ConvertProjectHandler extends SelectionResourceCommandHandler {
+
+ @Override
+ protected IResourceCommandHandler getResourceCommandHandler() {
+ return new ConvertProjectCommandHandler();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
new file mode 100644
index 0000000..0df3770
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshSigilClasspathCommandHandler implements
+ IResourceCommandHandler {
+
+ public Object execute(IResource[] resources, ExecutionEvent event)
+ throws ExecutionException {
+ try {
+ for ( IResource res : resources ) {
+ IProject p = (IProject) res;
+ final ISigilProjectModel model = SigilCore.create(p);
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException, InvocationTargetException,
+ InterruptedException {
+ model.resetClasspath(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to create sigil project for refresh action", e );
+ }
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
new file mode 100644
index 0000000..6dc20bb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class RefreshSigilClasspathHandler extends SelectionResourceCommandHandler {
+
+ @Override
+ protected IResourceCommandHandler getResourceCommandHandler() {
+ return new RefreshSigilClasspathCommandHandler();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
new file mode 100644
index 0000000..3f437bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.startup;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ResolveProjectsJob;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.ui.IStartup;
+
+public class SigilStartup implements IStartup {
+
+ public void earlyStartup() {
+ // Create a task to run the resolver
+ final Runnable resolver = new Runnable() {
+ public void run() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ ResolveProjectsJob job = new ResolveProjectsJob(workspace);
+ job.setSystem(true);
+ job.schedule();
+ }
+ };
+
+ // Register a repository change listener to re-run the resolver when repository changes
+ SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(new IRepositoryChangeListener() {
+ public void repositoryChanged(RepositoryChangeEvent event) {
+ resolver.run();
+ }
+ });
+
+ // Run the resolver now
+ resolver.run();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
new file mode 100644
index 0000000..a3c70d5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilUI extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.ui";
+
+ public static final String REPOSITORY_WIZARD_EXTENSION_POINT_ID = "org.cauldron.sigil.ui.repositorywizard";
+
+ public static final String PREF_NOPROMPT_INSTALL_COMPOSITE_WITH_ERRORS = "nopromptInstallCompositeError";
+ public static final String PREF_INSTALL_COMPOSITE_WITH_ERRORS_ANSWER = "answerInstallCompositeError";
+
+ public static final String ID_REPOSITORY_VIEW = "org.cauldron.sigil.ui.repositoryBrowser";
+ public static final String ID_DEPENDENCY_VIEW = "org.cauldron.sigil.ui.bundleDependencyView";
+ public static final String ID_INSTANCES_VIEW = "org.cauldron.sigil.runtime.newtonInstancesView";
+
+ // The shared instance
+ private static SigilUI plugin;
+
+ /**
+ * The constructor
+ */
+ public SigilUI() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ SigilCore.getDefault();
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static SigilUI getDefault() {
+ return plugin;
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ return ResourceBundle.getBundle( "resources." + SigilUI.class.getName(), Locale.getDefault(), SigilUI.class.getClassLoader() );
+ }
+
+ public static void runWorkspaceOperation(IRunnableWithProgress op, Shell shell) {
+ if ( shell == null ) {
+ shell = getActiveWorkbenchShell();
+ }
+ try {
+ new ProgressMonitorDialog(shell).run(true, true, op);
+ } catch (InvocationTargetException e) {
+ SigilCore.error( "Workspace operation failed", e);
+ } catch (InterruptedException e1) {
+ SigilCore.log( "Workspace operation interrupted");
+ }
+ }
+
+ public static void runWorkspaceOperationSync(IRunnableWithProgress op, Shell shell) throws Throwable {
+ if ( shell == null ) {
+ shell = getActiveWorkbenchShell();
+ }
+ try {
+ new ProgressMonitorDialog(shell).run(false, true, op);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ } catch (InterruptedException e1) {
+ SigilCore.log( "Workspace operation interrupted");
+ }
+ }
+ public static IWorkbenchWindow getActiveWorkbenchWindow() {
+ return getDefault().getWorkbench().getActiveWorkbenchWindow();
+ }
+
+ public static Shell getActiveWorkbenchShell() {
+ final Shell[] shell = new Shell[1];
+ runInUISync( new Runnable() {
+ public void run() {
+ shell[0] = getActiveDisplay().getActiveShell();
+ }
+ });
+ return shell[0];
+ }
+
+ public static Display getActiveDisplay() {
+ Display d = Display.getCurrent();
+
+ if ( d == null ) {
+ d = Display.getDefault();
+ }
+
+ return d;
+ }
+
+ public static void runInUI(Runnable runnable) {
+ getActiveDisplay().asyncExec(runnable);
+ }
+
+ public static void runInUISync(Runnable runnable) {
+ getActiveDisplay().syncExec(runnable);
+ }
+
+ public static Image cacheImage(String path, ClassLoader classLoader) {
+ ImageRegistry registry = SigilUI.getDefault().getImageRegistry();
+
+ Image image = registry.get( path );
+
+ if ( image == null ) {
+ image = loadImage( path, classLoader );
+ // XXX-FIXME-XXX add null image
+ if ( image != null ) {
+ registry.put( path, image);
+ }
+ }
+
+ return image;
+ }
+
+ private static Image loadImage(String resource, ClassLoader loader) {
+ InputStream in = loader.getResourceAsStream( resource );
+ if ( in != null ) {
+ ImageData data = new ImageData( in );
+ return new Image( SigilUI.getActiveDisplay(), data );
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
new file mode 100644
index 0000000..b8e7d2e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.completion;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+class CompletionProposal implements ICompletionProposal {
+
+ private int fReplacementOffset;
+ private int fReplacementLength;
+ private int fCursorPosition;
+ private String fReplacementString;
+ private String fAdditionalProposalInfo;
+ private IContextInformation fContextInformation;
+ private String fDisplayString;
+ private Image fImage;
+
+ /**
+ * Creates a new completion proposal based on the provided information. The replacement string is
+ * considered being the display string too. All remaining fields are set to <code>null</code>.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition) {
+ this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
+ }
+
+ /**
+ * Creates a new completion proposal. All fields are initialized based on the provided information.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ * @param image the image to display for this proposal
+ * @param displayString the string to be displayed for the proposal
+ * @param contextInformation the context information associated with this proposal
+ * @param additionalProposalInfo the additional information associated with this proposal
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+ fReplacementString= replacementString;
+ fReplacementOffset= replacementOffset;
+ fReplacementLength= replacementLength;
+ fCursorPosition= cursorPosition;
+ fImage= image;
+ fDisplayString= displayString;
+ fContextInformation= contextInformation;
+ fAdditionalProposalInfo= additionalProposalInfo;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public void apply( IDocument document ) {
+ try {
+ document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
+ } catch (BadLocationException x) {
+ // ignore
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return fAdditionalProposalInfo;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return fContextInformation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ if (fDisplayString != null)
+ return fDisplayString;
+ return fReplacementString;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public Point getSelection( IDocument document ) {
+ return new Point(fReplacementOffset + fCursorPosition, 0);
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
new file mode 100644
index 0000000..2ba92ca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.File;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.forms.IManagedForm;
+
+public abstract class AbstractResourceSection extends SigilSection implements IResourceChangeListener, ICheckStateListener {
+
+ protected static final Object[] EMPTY = new Object[]{};
+ protected Tree tree;
+ protected CheckboxTreeViewer viewer;
+ private IWorkspace workspace;
+
+ public AbstractResourceSection(SigilPage page, Composite parent,
+ ISigilProjectModel project) throws CoreException {
+ super(page, parent, project);
+ }
+
+ @Override
+ public void initialize(IManagedForm form) {
+ super.initialize(form);
+ viewer.setAllChecked(false);
+ viewer.setGrayedElements( EMPTY );
+ refreshSelections();
+ viewer.refresh();
+ }
+
+ @Override
+ public void refresh() {
+ refreshSelections();
+ super.refresh();
+ }
+
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ handleStateChanged( (IResource) event.getElement(), event.getChecked(), true, true );
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ if ( !getSection().getDisplay().isDisposed() ) {
+ getSection().getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( getSection().isVisible() ) {
+ viewer.refresh();
+ }
+ }
+ });
+ }
+ }
+
+ protected void startWorkspaceListener(IWorkspace workspace) {
+ this.workspace = workspace;
+ workspace.addResourceChangeListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ workspace.removeResourceChangeListener(this);
+ }
+
+ protected abstract void refreshSelections();
+
+ protected abstract void syncResourceModel(IResource element, boolean checked);
+
+ protected IResource findResource(IPath path) {
+ IProject project2 = getProjectModel().getProject();
+ File f = project2.getLocation().append(path).toFile();
+
+ if ( f.exists() ) {
+ try {
+ if ( f.isFile() ) {
+ return project2.getFile(path);
+ }
+ else {
+ return project2.getFolder(path);
+ }
+ }
+ catch (IllegalArgumentException e) {
+ SigilCore.error( "Unknown path " + path );
+ return null;
+ }
+ }
+ else {
+ SigilCore.error( "Unknown file " + f );
+ return null;
+ }
+ }
+
+
+ protected void handleStateChanged(IResource element, boolean checked, boolean recurse,
+ boolean sync) {
+ if ( element instanceof IContainer ) {
+ setParentsGrayChecked(element, checked);
+ if ( recurse ) {
+ recursiveCheck( (IContainer) element, checked, sync );
+ }
+ }
+ else {
+ setParentsGrayChecked(element, checked);
+ }
+
+ if ( sync ) {
+ syncResourceModel( element, checked );
+ }
+ }
+
+ private void recursiveCheck(IContainer element, final boolean checked, final boolean sync) {
+ try {
+ element.accept( new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ viewer.setChecked(resource, checked);
+ if ( sync ) {
+ syncResourceModel(resource, checked);
+ }
+ return true;
+ }
+ } );
+ }
+ catch ( CoreException e ) {
+ DebugPlugin.log( e.getStatus() );
+ }
+ }
+
+ private void setParentsGrayChecked(IResource r, boolean checked) {
+ while ( r.getParent() != null ) {
+ r = r.getParent();
+ if ( (viewer.getGrayed(r) && viewer.getChecked(r)) == checked) {
+ break;
+ }
+ if ( viewer.getChecked(r) == checked ) {
+ viewer.setGrayed( r, !checked );
+ }
+ else {
+ viewer.setGrayChecked(r, checked);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
new file mode 100644
index 0000000..0750e0f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public abstract class BundleDependencySection extends SigilSection {
+
+ private Set<? extends IModelElement> unresolvedElements;
+
+ protected ProjectTableViewer viewer;
+ private Button add;
+ private Button edit;
+ private Button remove;
+
+ public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+ super(page, parent, project);
+ viewer.setUnresolvedElements(unresolvedElements);
+ }
+
+ public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ this(page, parent, project, null);
+ }
+
+ protected abstract String getTitle();
+
+ protected abstract Label createLabel(Composite parent, FormToolkit toolkit);
+
+ protected abstract IContentProvider getContentProvider();
+
+ protected abstract void handleAdd();
+
+ protected abstract void handleEdit();
+
+ protected abstract void handleRemoved();
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ viewer.refresh();
+ }
+
+ protected ISelection getSelection() {
+ return viewer.getSelection();
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( getTitle() );
+
+ Composite body = createGridBody(2, false, toolkit);
+
+ Label label = createLabel(body, toolkit);
+
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+
+ Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+ GridData data = new GridData( GridData.FILL_BOTH );
+ data.heightHint = 600;
+ bundleTable.setLayoutData( data );
+ bundleTable.setLinesVisible(false);
+ createButtons(body, toolkit);
+ createViewer( bundleTable );
+ }
+
+ protected void createButtons(Composite body, FormToolkit toolkit) {
+ Composite buttons = toolkit.createComposite(body);
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.numColumns = 1;
+ layout.topMargin = 0;
+ layout.leftMargin = 0;
+ layout.rightMargin = 0;
+ layout.bottomMargin = 0;
+ buttons.setLayout( layout );
+ GridData data = new GridData();
+ data.verticalAlignment = SWT.TOP;
+ buttons.setLayoutData(data);
+
+ add = toolkit.createButton(buttons, "Add", SWT.NULL);
+ add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ } );
+
+ edit = toolkit.createButton(buttons, "Edit", SWT.NULL);
+ edit.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ } );
+
+ remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+ remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemoved();
+ }
+ } );
+
+ setSelected(false);
+ }
+
+ protected void createViewer(Table bundleTable) {
+ viewer = new ProjectTableViewer(bundleTable);
+ viewer.setContentProvider(getContentProvider());
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setSelected(!event.getSelection().isEmpty());
+ }
+ });
+ }
+
+ private void setSelected(boolean selected) {
+ edit.setEnabled(selected);
+ remove.setEnabled(selected);
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
new file mode 100644
index 0000000..311acca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.IFilter;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ClasspathSection extends SigilSection {
+
+ private ProjectTableViewer viewer;
+ private final Comparator<IClasspathEntry> CLASSPATH_COMPARATOR = new Comparator<IClasspathEntry>() {
+ public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+ return compareClasspaths(o1, o2);
+ }
+ };
+
+ public ClasspathSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Classpath");
+
+ Composite body = createGridBody(2, false, toolkit);
+
+ Label label = toolkit.createLabel( body, "Specify the internal classpath of this bundle." );
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+
+ Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+ GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ tableLayoutData.heightHint = 150;
+ bundleTable.setLayoutData(tableLayoutData);
+
+ createButtons(body, toolkit);
+ createViewer( bundleTable );
+ }
+
+ private void createButtons(Composite body, FormToolkit toolkit) {
+ Composite buttons = toolkit.createComposite(body);
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.numColumns = 1;
+ layout.topMargin = 0;
+ layout.leftMargin = 0;
+ layout.rightMargin = 0;
+ layout.bottomMargin = 0;
+ buttons.setLayout( layout );
+
+ Button add = toolkit.createButton(buttons, "Add", SWT.NULL);
+ add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ } );
+
+ Button remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+ remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemoved();
+ }
+ } );
+ }
+
+ private void createViewer(Table bundleTable) {
+ viewer = new ProjectTableViewer(bundleTable);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ ArrayList<IClasspathEntry> cp = new ArrayList<IClasspathEntry>();
+ for (IClasspathEntry cpe : JavaHelper.findClasspathEntries(getBundle())) {
+ cp.add(cpe);
+ }
+
+ Collections.sort(cp, new Comparator<IClasspathEntry>() {
+ public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+ return o1.toString().compareTo(o2.toString());
+ }
+ });
+ return cp.toArray();
+ }
+ });
+ viewer.setComparator(new ViewerComparator() {
+ @Override
+ public int category(Object element) {
+ return index((IClasspathEntry) element);
+ }
+ });
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ private void handleAdd() {
+ try {
+ BackgroundLoadingSelectionDialog<IClasspathEntry> dialog = new BackgroundLoadingSelectionDialog<IClasspathEntry>(getSection().getShell(), "Classpath Entry:", true);
+
+ dialog.setDescriptor(new IElementDescriptor<IClasspathEntry>() {
+ public String getName(IClasspathEntry element) {
+ return element.getPath().toString();
+ }
+
+ public String getLabel(IClasspathEntry element) {
+ return getName(element);
+ }
+ });
+
+ dialog.setLabelProvider(new ModelLabelProvider());
+
+ dialog.setFilter(new IFilter<IClasspathEntry>() {
+ public boolean select(IClasspathEntry cp) {
+ switch ( cp.getEntryKind() ) {
+ case IClasspathEntry.CPE_LIBRARY:
+ case IClasspathEntry.CPE_VARIABLE:
+ case IClasspathEntry.CPE_SOURCE:
+ return !getBundle().getClasspathEntrys().contains(encode(cp));
+ default:
+ return false;
+ }
+ }
+ });
+
+ dialog.setComparator(CLASSPATH_COMPARATOR);
+
+ IClasspathEntry[] classpath = getProjectModel().getJavaModel().getRawClasspath();
+ dialog.addElements(Arrays.asList(classpath));
+ if ( dialog.open() == Window.OK ) {
+ List<IClasspathEntry> selectedElements = dialog.getSelectedElements();
+
+ Object[] added = selectedElements.toArray();
+ for (IClasspathEntry entry : selectedElements) {
+ getBundle().addClasspathEntry(encode(entry));
+ }
+ viewer.add(added);
+ viewer.refresh();
+ markDirty();
+ }
+ } catch (JavaModelException e) {
+ ErrorDialog.openError(getSection().getShell(), "Error", null, e.getStatus());
+ }
+ }
+
+ private int compareClasspaths(IClasspathEntry o1, IClasspathEntry o2) {
+ if ( o1.getEntryKind() == o2.getEntryKind() ) {
+ ModelLabelProvider mlp = viewer.getLabelProvider();
+ return mlp.getText(o1).compareTo(mlp.getText(o2));
+ }
+ else {
+ int i1 = index(o1);
+ int i2 = index(o2);
+
+ if ( i1 < i2 ) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }
+ }
+
+ private static int index(IClasspathEntry o1) {
+ switch ( o1.getEntryKind() ) {
+ case IClasspathEntry.CPE_SOURCE: return 0;
+ case IClasspathEntry.CPE_PROJECT: return 1;
+ case IClasspathEntry.CPE_LIBRARY: return 2;
+ case IClasspathEntry.CPE_VARIABLE: return 3;
+ case IClasspathEntry.CPE_CONTAINER: return 4;
+ default: throw new IllegalStateException( "Unknown classpath entry type " + o1);
+ }
+ }
+
+ private String encode(IClasspathEntry cp) {
+ return getProjectModel().getJavaModel().encodeClasspathEntry(cp).trim();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IClasspathEntry> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().removeClasspathEntry(getProjectModel().getJavaModel().encodeClasspathEntry(i.next()).trim());
+ }
+ viewer.remove(selection.toArray());
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
new file mode 100644
index 0000000..5d091fb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+
+public class ContainerTreeProvider extends DefaultTreeContentProvider {
+
+ private static final Object[] EMPTY = new Object[] {};
+
+ public Object[] getChildren(Object parentElement) {
+ if ( parentElement instanceof IContainer ) {
+ IContainer f = (IContainer) parentElement;
+ try {
+ return f.members();
+ } catch (CoreException e) {
+ DebugPlugin.log( e.getStatus() );
+ }
+ }
+ return EMPTY;
+ }
+
+ public Object getParent(Object element) {
+ IResource r = (IResource) element;
+ return r.getParent();
+ }
+
+ public boolean hasChildren(Object element) {
+ if ( element instanceof IContainer ) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public Object[] getElements(Object inputElement) {
+ IContainer container = (IContainer) inputElement;
+ return getChildren(container);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
new file mode 100644
index 0000000..7d1fbcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ContentSummarySection extends SigilSection {
+
+ public ContentSummarySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Project Content");
+
+ Composite body = createTableWrapBody(2, toolkit);
+ Hyperlink link = toolkit.createHyperlink( body, "Contents:", SWT.NONE );
+ link.setHref( ContentsForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the content that this bundle provides." );
+
+ link = toolkit.createHyperlink( body, "Dependencies:", SWT.NONE );
+ link.setHref( DependenciesForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the dependencies that this bundle needs to run." );
+
+ link = toolkit.createHyperlink( body, "Exports:", SWT.NONE );
+ link.setHref( ExportsForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the resources that this bundle exports." );
+ }
+
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ getPage().getEditor().setActivePage( (String) e.getHref() );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
new file mode 100644
index 0000000..62d58a8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ContentsForm extends SigilPage {
+
+ public static final String PAGE_ID = "contents";
+
+ private ISigilProjectModel project;
+
+ public ContentsForm(FormEditor editor, ISigilProjectModel project) {
+ super(editor, PAGE_ID, "Contents");
+ this.project = project;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Contents" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ Composite top = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ top.setLayout(layout);
+ TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+ data.colspan = 2;
+ top.setLayoutData(data);
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ try {
+ ClasspathSection classpath = new ClasspathSection( this, top, project );
+ managedForm.addPart( classpath );
+
+ ResourceBuildSection runtimeBuild = new ResourceBuildSection( this, left, project );
+ managedForm.addPart( runtimeBuild );
+
+ DownloadSection download = new DownloadSection( this, right, project );
+ managedForm.addPart( download );
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create contents form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
new file mode 100644
index 0000000..1c28ba5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class DependenciesForm extends SigilPage {
+
+ public static final String PAGE_ID = "dependencies";
+
+ private final ISigilProjectModel project;
+ private final Set<IModelElement> unresolvedElements;
+
+ private ImportPackagesSection importPackages;
+ private RequiresBundleSection requiresSection;
+
+
+ public DependenciesForm(FormEditor editor, ISigilProjectModel project, Set<IModelElement> unresolved) {
+ super(editor, PAGE_ID, "Dependencies");
+ this.project = project;
+ this.unresolvedElements = unresolved;
+ }
+
+ @Override
+ public boolean canLeaveThePage() {
+ return !isDirty();
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Dependencies" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB);
+ layoutData.rowspan = 2;
+ left.setLayoutData(layoutData);
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData(new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ Composite bottom = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ bottom.setLayout(layout);
+ bottom.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ try {
+ importPackages = new ImportPackagesSection( this, left, project, unresolvedElements );
+ managedForm.addPart( importPackages );
+
+ requiresSection = new RequiresBundleSection(this, right, project, unresolvedElements );
+ managedForm.addPart(requiresSection);
+
+ DependencyManagementSection depMgmt = new DependencyManagementSection(this, bottom, project);
+ managedForm.addPart(depMgmt);
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create dependencies form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
new file mode 100644
index 0000000..aadf990
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.AccumulatorAdapter;
+import org.cauldron.sigil.ui.util.ExportedPackageFinder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IImportDeclaration;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class DependencyManagementSection extends SigilSection {
+
+ private Hyperlink hypConvertRBtoIP;
+
+ public DependencyManagementSection(SigilPage page, Composite parent,
+ ISigilProjectModel project) throws CoreException {
+ super(page, parent, project);
+ }
+
+ protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+ setTitle("Dependency Management");
+
+ Composite body = createGridBody(1, false, toolkit);
+
+ hypConvertRBtoIP = toolkit.createHyperlink(body, "Convert Required Bundles to Imported Packages", SWT.NONE);
+ hypConvertRBtoIP.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ hypConvertRBtoIP.addHyperlinkListener(new HyperlinkAdapter() {
+ public void linkActivated(HyperlinkEvent e) {
+ run();
+ }
+ });
+ }
+
+ protected void run() {
+ final Map<String, IPackageExport> exports = new HashMap<String, IPackageExport>();
+ final Set<String> imports = new HashSet<String>();
+
+ // Find all exports
+ final ExportedPackageFinder exportFinder = new ExportedPackageFinder(getProjectModel(), new AccumulatorAdapter<IPackageExport>() {
+ public void addElements(Collection<? extends IPackageExport> elements) {
+ for (IPackageExport export : elements) {
+ exports.put(export.getPackageName(), export);
+ }
+ }
+ });
+ Job findExportsJob = new Job("Find exports") {
+ protected IStatus run(IProgressMonitor monitor) {
+ return exportFinder.run(monitor);
+ }
+ };
+ findExportsJob.setUser(true);
+ findExportsJob.schedule();
+
+ // Find imports from Java source
+ Job findImportsJob = new Job("Find imports") {
+ protected IStatus run(IProgressMonitor monitor) {
+ IJavaProject javaProject = getProjectModel().getJavaModel();
+ try {
+ IPackageFragment[] packages = javaProject.getPackageFragments();
+ for (IPackageFragment pkg : packages) {
+ ICompilationUnit[] compilationUnits = pkg.getCompilationUnits();
+ for (ICompilationUnit compilationUnit : compilationUnits) {
+ IImportDeclaration[] importDecls = compilationUnit.getImports();
+ for (IImportDeclaration importDecl : importDecls) {
+ imports.add(getPackageName(importDecl));
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return new Status(IStatus.ERROR, SigilUI.PLUGIN_ID, 0, "Error finding imports", e);
+ }
+ }
+ };
+ findImportsJob.setUser(true);
+ findImportsJob.schedule();
+
+ // Wait for both jobs to complete
+ try {
+ findImportsJob.join();
+ findExportsJob.join();
+ } catch (InterruptedException e) {
+ // Aborted, just do nothing
+ return;
+ }
+
+ // Get the version rules
+ IPreferenceStore prefStore = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ // Get the existing imports for the bundle
+ IBundleModelElement bundleInfo = getProjectModel().getBundle().getBundleInfo();
+ Set<IPackageImport> existingImports = bundleInfo.getImports();
+ Map<String, IPackageImport> existingImportsMap = new HashMap<String, IPackageImport>();
+ for (IPackageImport existingImport : existingImports) {
+ existingImportsMap.put(existingImport.getPackageName(), existingImport);
+ }
+
+ // Add imports to the bundle
+ ModelElementFactory elementFactory = ModelElementFactory.getInstance();
+ int count = 0;
+ for (String pkgImport : imports) {
+ IPackageExport export = exports.get(pkgImport);
+ if(export != null && !existingImportsMap.containsKey(pkgImport)) {
+ VersionRange versionRange = VersionRange.newInstance(export.getVersion(), lowerBoundRule, upperBoundRule);
+ IPackageImport newImport = elementFactory.newModelElement(IPackageImport.class);
+ newImport.setPackageName(pkgImport);
+ newImport.setVersions(versionRange);
+ newImport.setOptional(false);
+
+ bundleInfo.addImport(newImport);
+ count++;
+ }
+ }
+
+ // Remove required bundles
+ Set<IRequiredBundle> requiredBundles = bundleInfo.getRequiredBundles();
+ int requiredBundlesSize = requiredBundles.size();
+ for (IRequiredBundle requiredBundle : requiredBundles) {
+ bundleInfo.removeRequiredBundle(requiredBundle);
+ }
+
+ // Update the editor
+ if(count + requiredBundlesSize > 0) {
+ IFormPart[] parts = getPage().getManagedForm().getParts();
+ for (IFormPart formPart : parts) {
+ formPart.refresh();
+ ((AbstractFormPart) formPart).markDirty();
+ }
+ }
+
+ MessageDialog.openInformation(getManagedForm().getForm().getShell(), "Dependency Management", "Removed " + requiredBundlesSize + " required bundle(s) and added " + count + " imported package(s).");
+ }
+
+ private static String getPackageName(IImportDeclaration decl) {
+ String name = decl.getElementName();
+ int lastDot = name.lastIndexOf('.');
+ return name.substring(0, lastDot);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
new file mode 100644
index 0000000..ad2e705
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class DownloadSection extends AbstractResourceSection {
+
+ /**
+ * @param page
+ * @param parent
+ * @param project
+ * @throws CoreException
+ */
+
+ private IDownloadJar dl;
+
+ public DownloadSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+ setTitle( "Codebase" );
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Specify which resources are included as part of this bundles remote codebase." );
+
+ tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+
+ TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+ data.heightHint = 200;
+ tree.setLayoutData( data );
+
+ viewer = new CheckboxTreeViewer( tree );
+ IFolder base = ResourcesPlugin.getWorkspace().getRoot().getFolder(getProjectModel().getJavaModel().getOutputLocation());
+ viewer.setContentProvider( new ContainerTreeProvider() );
+ viewer.setLabelProvider( new ModelLabelProvider() );
+ viewer.addCheckStateListener( this );
+ viewer.setInput( base );
+
+ dl = getProjectModel().getBundle().getDownloadJar();
+
+ startWorkspaceListener(base.getWorkspace());
+ }
+
+ @Override
+ public void refresh() {
+ dl = getProjectModel().getBundle().getDownloadJar();
+ super.refresh();
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ getProjectModel().getBundle().setDownloadJar(dl);
+ super.commit(onSave);
+ }
+
+ @Override
+ protected void refreshSelections() {
+ // zero the state
+ if ( dl != null ) {
+ for ( IPath path : dl.getEntrys() ) {
+ IResource r = findResource( path );
+ if ( r != null ) {
+ viewer.expandToLevel(r, 0);
+ viewer.setChecked( r, true );
+ viewer.setGrayed( r, false );
+ handleStateChanged(r, true, false, false);
+ }
+ else {
+ SigilCore.error( "Unknown path " + path );
+ }
+ }
+ }
+ }
+
+ protected void syncResourceModel(IResource element, boolean checked) {
+ try {
+ if ( dl == null ) {
+ dl = ModelElementFactory.getInstance().newModelElement(IDownloadJar.class);
+ getProjectModel().getBundle().setDownloadJar(dl);
+ }
+
+ if ( checked ) {
+ dl.addEntry( element.getProjectRelativePath() );
+ }
+ else {
+ dl.removeEntry( element.getProjectRelativePath() );
+ }
+
+ markDirty();
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to create model element", e );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
new file mode 100644
index 0000000..4f91445
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class ExcludedResourcesFilter extends ViewerFilter {
+
+ private final Set<Pattern> exclusionSet = new HashSet<Pattern>();
+
+ public ExcludedResourcesFilter() {
+ loadExclusions();
+ }
+
+ public final synchronized void loadExclusions() {
+ exclusionSet.clear();
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ String[] exclusions = PrefsUtils.stringToArray(store.getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES));
+ for (String exclusion : exclusions) {
+ exclusionSet.add(GlobCompiler.compile(exclusion));
+ }
+ }
+
+ @Override
+ public synchronized boolean select(Viewer viewer, Object parentElement, Object element) {
+ IResource file = (IResource) element;
+ String path = file.getName();
+ for ( Pattern p :exclusionSet ) {
+ if ( p.matcher(path).matches() ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
new file mode 100644
index 0000000..406a5e5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.osgi.framework.Version;
+
+public class ExportPackagesSection extends BundleDependencySection {
+
+ public ExportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Export Packages";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which packages this bundle shares with other bundles." );
+ }
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return getBundle().getBundleInfo().getExports().toArray();
+ }
+ };
+ }
+
+ @Override
+ protected void handleAdd() {
+ NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Add Exported Package", null, getProjectModel(), true);
+
+ if ( dialog.open() == Window.OK ) {
+ try {
+ // Add selected exports
+ boolean exportsAdded = false;
+
+ List<IPackageFragment> newPkgFragments = dialog.getSelectedElements();
+ for (IPackageFragment pkgFragment : newPkgFragments) {
+ IPackageExport pkgExport = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pkgExport.setPackageName(pkgFragment.getElementName());
+ pkgExport.setVersion(dialog.getVersion());
+ getBundle().getBundleInfo().addExport(pkgExport);
+
+ exportsAdded = true;
+ }
+
+ // Add corresponding imports (maybe)
+ boolean importsAdded = false;
+
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ boolean shouldAddImports = OptionalPrompt.optionallyPrompt(store, SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Exports", "Should corresponding imports be added?", getSection().getShell());
+ if(shouldAddImports) {
+ for (IPackageFragment pkgFragment : newPkgFragments) {
+ IPackageImport pkgImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pkgImport.setPackageName(pkgFragment.getElementName());
+ VersionRangeBoundingRule lowerBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+ Version version = dialog.getVersion();
+ if(version == null) {
+ version = getBundle().getVersion();
+ }
+ VersionRange versionRange = VersionRange.newInstance(version, lowerBound, upperBound);
+ pkgImport.setVersions(versionRange);
+
+ getBundle().getBundleInfo().addImport(pkgImport);
+
+ importsAdded = true;
+ }
+ }
+
+ if(importsAdded) {
+ ((SigilProjectEditorPart) getPage().getEditor()).refreshAllPages();
+ markDirty();
+ } else if(exportsAdded) {
+ refresh();
+ markDirty();
+ }
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to buiild model element for package export", e);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageExport packageExport = i.next();
+ NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Edit Imported Package", packageExport, getProjectModel(), false);
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ IPackageFragment pkgFragment = dialog.getSelectedElement();
+ packageExport.setPackageName(pkgFragment.getElementName());
+ packageExport.setVersion(dialog.getVersion());
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeExport( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+
+ private ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
new file mode 100644
index 0000000..7a6fe3c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ExportsForm extends SigilPage {
+
+ public static final String PAGE_ID = "exports";
+
+ private ISigilProjectModel project;
+
+ public ExportsForm(FormEditor editor, ISigilProjectModel project) {
+ super(editor, PAGE_ID, "Exports");
+ this.project = project;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Exports" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 1;
+ layout.horizontalSpacing = 10;
+ layout.verticalSpacing = 20;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ try {
+ ExportPackagesSection exportPackages = new ExportPackagesSection( this, body, project );
+ managedForm.addPart( exportPackages );
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create contents form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
new file mode 100644
index 0000000..4200712
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.IFormValueConverter;
+import org.cauldron.sigil.ui.form.SigilFormEntry;
+import org.cauldron.sigil.ui.form.SigilFormEntryAdapter;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class GeneralInfoSection extends SigilSection {
+
+ private String name;
+ private String symbolicName;
+ private Version version;
+ private String description;
+ private String provider;
+ private String activator;
+ private IRequiredBundle fragmentHost;
+
+ private SigilFormEntry nameEntry;
+ private SigilFormEntry symbolicNameEntry;
+ private SigilFormEntry versionEntry;
+ private SigilFormEntry descriptionEntry;
+ private SigilFormEntry providerEntry;
+ private SigilFormEntry activatorEntry;
+ private SigilFormEntry fragmentHostEntry;
+
+ /**
+ * @param parent
+ * @param toolkit
+ * @param style
+ * @throws CoreException
+ */
+ public GeneralInfoSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("General Information");
+
+ Composite body = createGridBody(3, false, toolkit);
+
+ Label label = toolkit.createLabel( body, "This section describes general information about this project." );
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, false, 3, 1) );
+
+ symbolicNameEntry = new SigilFormEntry(body, toolkit, "Symbolic Name");
+ symbolicNameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ symbolicName = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ nameEntry = new SigilFormEntry(body, toolkit, "Name");
+ nameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ name = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ descriptionEntry = new SigilFormEntry(body, toolkit, "Description");
+ descriptionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ description = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ IFormValueConverter converter = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ Version v = (Version) value;
+ return v.toString();
+ }
+
+ public Object getValue(String label) {
+ return Version.parseVersion(label);
+ }
+ };
+
+ versionEntry = new SigilFormEntry(body, toolkit, "Version", null, converter);
+ versionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ version = (Version) form.getValue();
+ checkDirty();
+ }
+ });
+
+ providerEntry = new SigilFormEntry(body, toolkit, "Provider");
+ providerEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ provider = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ activatorEntry = new SigilFormEntry(body, toolkit, "Bundle Activator", "Browse...", null);
+ activatorEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ activator = (String) form.getValue();
+ checkDirty();
+ }
+
+ @Override
+ public void browseButtonSelected(SigilFormEntry form) {
+ BackgroundLoadingSelectionDialog<String> dialog = ResourcesDialogHelper.createClassSelectDialog(getShell(), "Add Bundle Activator", getProjectModel(), activator, BundleActivator.class.getName());
+
+ if (dialog.open() == Window.OK) {
+ form.setValue(dialog.getSelectedElement());
+ }
+ }
+ });
+
+ converter = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ IRequiredBundle b = (IRequiredBundle) value;
+ return b == null ? null : b.getSymbolicName() + " " + b.getVersions();
+ }
+
+ public Object getValue(String label) {
+ return null;
+ }
+ };
+
+ fragmentHostEntry = new SigilFormEntry(body, toolkit, "Fragment Host", "Browse...", converter );
+ fragmentHostEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ fragmentHost = (IRequiredBundle) form.getValue();
+ checkDirty();
+ }
+
+ @Override
+ public void browseButtonSelected(SigilFormEntry form) {
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+
+ if (dialog.open() == Window.OK) {
+ IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+ required.setSymbolicName(dialog.getSelectedName());
+ required.setVersions(dialog.getSelectedVersions());
+ form.setValue(required);
+ }
+ }
+ });
+ fragmentHostEntry.setFreeText(false);
+ }
+
+ private static String nullIfEmpty(String value) {
+ if ( value.trim().length() == 0 ) {
+ return null;
+ }
+ return value;
+ }
+
+ private Shell getShell() {
+ return getSection().getShell();
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ getBundle().getBundleInfo().setSymbolicName( symbolicName );
+ getBundle().getBundleInfo().setName( name );
+ getBundle().getBundleInfo().setVersion( version );
+ getBundle().getBundleInfo().setDescription( description );
+ getBundle().getBundleInfo().setVendor( provider );
+ getBundle().getBundleInfo().setFragmentHost(fragmentHost);
+ getBundle().getBundleInfo().setActivator(activator);
+
+ super.commit(onSave);
+ }
+
+ @Override
+ public void refresh() {
+ symbolicName = getProjectModel().getBundle().getBundleInfo().getSymbolicName();
+ name = getProjectModel().getBundle().getBundleInfo().getName();
+ description = getProjectModel().getBundle().getBundleInfo().getDescription();
+ version = getProjectModel().getBundle().getBundleInfo().getVersion();
+ provider = getProjectModel().getBundle().getBundleInfo().getVendor();
+ fragmentHost = getProjectModel().getBundle().getBundleInfo().getFragmentHost();
+ activator = getProjectModel().getBundle().getBundleInfo().getActivator();
+
+ nameEntry.setValue(name);
+ symbolicNameEntry.setValue(symbolicName);
+ versionEntry.setValue(version);
+ descriptionEntry.setValue(description);
+ providerEntry.setValue(provider);
+ fragmentHostEntry.setValue(fragmentHost);
+ activatorEntry.setValue(activator);
+
+ super.refresh();
+ }
+
+ private void checkDirty() {
+ boolean dirty = different(symbolicName, getProjectModel().getBundle().getBundleInfo().getSymbolicName() ) ||
+ different(name, getProjectModel().getBundle().getBundleInfo().getName() ) ||
+ different(version, getProjectModel().getBundle().getBundleInfo().getVersion() ) ||
+ different(description, getProjectModel().getBundle().getBundleInfo().getDescription()) ||
+ different(provider, getProjectModel().getBundle().getBundleInfo().getVendor()) ||
+ different(fragmentHost, getProjectModel().getBundle().getBundleInfo().getFragmentHost()) ||
+ different(activator, getProjectModel().getBundle().getBundleInfo().getActivator());
+
+ if ( dirty ) markDirty();
+ }
+
+ private boolean different(Object val1, Object val2) {
+ return val1 == null ? val2 != null : !val1.equals( val2 );
+ }
+
+ private ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
new file mode 100644
index 0000000..556c5e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IDependencyChecker {
+ boolean isSatisfied(IPackageImport packageImport);
+ boolean isSatisfied(IRequiredBundle requiredBundle);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
new file mode 100644
index 0000000..4aa2953
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+public interface IElementDescriptor<E> {
+ /**
+ * Return the short identifying name of the element.
+ */
+ String getName(E element);
+
+ /**
+ * Return a label for the element, including the name but possibly supplying
+ * additional information.
+ *
+ * @param element
+ * @return
+ */
+ String getLabel(E element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
new file mode 100644
index 0000000..9403075
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class ImportPackagesSection extends BundleDependencySection {
+
+ public ImportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedPackages) throws CoreException {
+ super( page, parent, project, unresolvedPackages );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Import Packages";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which packages this bundle imports from other bundles." );
+ }
+
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ ArrayList<IPackageImport> imports = new ArrayList<IPackageImport>(getBundle().getBundleInfo().getImports());
+ Collections.sort(imports, new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo( o2.getPackageName() );
+ }
+ });
+ return imports.toArray();
+ }
+ };
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ @Override
+ protected void handleAdd() {
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog =
+ ResourcesDialogHelper.createImportDialog(
+ getSection().getShell(),
+ "Add Imported Package",
+ getProjectModel(),
+ null,
+ getBundle().getBundleInfo().getImports() );
+
+ if ( dialog.open() == Window.OK ) {
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName(dialog.getSelectedName());
+ pi.setVersions(dialog.getSelectedVersions());
+ pi.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().addImport(pi);
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeImport( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageImport packageImport = i.next();
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog =
+ ResourcesDialogHelper.createImportDialog(
+ getSection().getShell(),
+ "Edit Imported Package",
+ getProjectModel(),
+ packageImport,
+ getBundle().getBundleInfo().getImports() );
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ newImport.setPackageName(dialog.getSelectedName());
+ newImport.setVersions(dialog.getSelectedVersions());
+ newImport.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().removeImport( packageImport );
+ getBundle().getBundleInfo().addImport(newImport);
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
new file mode 100644
index 0000000..02289d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Comparator;
+
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class NewPackageExportDialog extends BackgroundLoadingSelectionDialog<IPackageFragment> {
+
+ private static final IElementDescriptor<IPackageFragment> PKG_FRAGMENT_STRINGIFIER = new IElementDescriptor<IPackageFragment>() {
+ public String getLabel(IPackageFragment element) {
+ return getName(element);
+ }
+ public String getName(IPackageFragment element) {
+ return element.getElementName();
+ }
+ };
+
+ private static final Comparator<IPackageFragment> PKG_FRAGMENT_COMPARATOR = new Comparator<IPackageFragment>() {
+ public int compare(IPackageFragment o1, IPackageFragment o2) {
+ return o1.getElementName().compareTo(o2.getElementName());
+ }
+ };
+
+ private Version version = null;
+ private String error = null;
+ private Version projectVersion = new Version(0,0,0);
+
+ private Button btnInheritBundleVersion;
+ private Button btnExplicitVersion;
+ private Text txtVersion;
+
+
+ public NewPackageExportDialog(Shell parentShell, boolean multiSelect) {
+ super(parentShell, "Package:", multiSelect);
+ setDescriptor(PKG_FRAGMENT_STRINGIFIER);
+ setComparator(PKG_FRAGMENT_COMPARATOR);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ Group grpVersion = new Group(composite, SWT.NONE);
+ grpVersion.setText("Version");
+
+ btnInheritBundleVersion = new Button(grpVersion, SWT.RADIO);
+ btnInheritBundleVersion.setText("Inherit bundle version");
+ new Label(grpVersion, SWT.NONE); // Spacer
+ btnExplicitVersion = new Button(grpVersion, SWT.RADIO);
+ btnExplicitVersion.setText("Fixed version:");
+ txtVersion = new Text(grpVersion, SWT.BORDER);
+
+ // Initialize
+ if(version == null) {
+ btnInheritBundleVersion.setSelection(true);
+ txtVersion.setEnabled(false);
+ txtVersion.setText(projectVersion.toString());
+ } else {
+ btnExplicitVersion.setSelection(true);
+ txtVersion.setEnabled(true);
+ txtVersion.setText(version.toString());
+ }
+ updateButtons();
+
+ // Listeners
+ Listener radioAndTextListener = new Listener() {
+ public void handleEvent(Event event) {
+ error = null;
+ if(btnInheritBundleVersion.getSelection()) {
+ version = null;
+ txtVersion.setEnabled(false);
+ } else {
+ txtVersion.setEnabled(true);
+ try {
+ version = new Version(txtVersion.getText());
+ } catch (IllegalArgumentException e) {
+ error = "Invalid version";
+ }
+ }
+ setErrorMessage(error);
+ updateButtons();
+ }
+ };
+ txtVersion.addListener(SWT.Modify, radioAndTextListener);
+ btnInheritBundleVersion.addListener(SWT.Selection, radioAndTextListener);
+ btnExplicitVersion.addListener(SWT.Selection, radioAndTextListener);
+
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ composite.setLayout(new GridLayout(1, false));
+ grpVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ grpVersion.setLayout(new GridLayout(2, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ return container;
+ }
+
+ @Override
+ protected boolean canComplete() {
+ return super.canComplete() && error == null;
+ }
+
+ public void setProjectVersion(Version projectVersion) {
+ this.projectVersion = projectVersion;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
new file mode 100644
index 0000000..59812cc
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IVersionedModelElement;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.Version;
+
+public class NewResourceSelectionDialog<E extends IVersionedModelElement> extends BackgroundLoadingSelectionDialog<E> {
+
+ private VersionRangeComponent pnlVersionRange;
+ private boolean optionalEnabled = true;
+ private Button btnOptional;
+
+ private VersionRange selectedVersions = null;
+ private boolean optional = false;
+
+ public NewResourceSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+ super(parentShell, selectionLabel, multi);
+ }
+
+ public void setOptionalEnabled(boolean enabled) {
+ optionalEnabled = enabled;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ if ( optionalEnabled ) {
+ new Label(composite, SWT.NONE); //Spacer
+ btnOptional = new Button(composite, SWT.CHECK);
+ btnOptional.setText("Optional");
+ }
+
+ Label lblVersionRange = new Label(composite, SWT.NONE);
+ lblVersionRange.setText("Version Range:");
+ Group group = new Group(composite, SWT.BORDER);
+
+ pnlVersionRange = new VersionRangeComponent(group, SWT.NONE);
+
+ // Initialize
+ if (selectedVersions != null) {
+ pnlVersionRange.setVersions(selectedVersions);
+ }
+
+ if ( optionalEnabled ) {
+ btnOptional.setSelection(optional);
+ updateButtons();
+ }
+
+ // Hookup Listeners
+ pnlVersionRange.addVersionChangeListener(new VersionsChangeListener() {
+ public void versionsChanged(VersionRange range) {
+ selectedVersions = range;
+ updateButtons();
+ }
+ });
+ pnlVersionRange.addValidationListener(new IValidationListener() {
+ public void validationMessage(String message, int level) {
+ setMessage(message, level);
+ updateButtons();
+ }
+ });
+
+ if ( optionalEnabled ) {
+ btnOptional.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ optional = btnOptional.getSelection();
+ }
+ });
+ }
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ GridLayout layout = new GridLayout(2, false);
+ layout.verticalSpacing = 10;
+ layout.horizontalSpacing = 10;
+ composite.setLayout(layout);
+
+ lblVersionRange.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ group.setLayout(new FillLayout());
+
+ return container;
+ }
+
+ @Override
+ protected void elementSelected(E selection) {
+ if(selection != null) {
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = selection.getVersion();
+ selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ pnlVersionRange.setVersions(selectedVersions);
+ }
+ }
+
+ @Override
+ protected synchronized boolean canComplete() {
+ return super.canComplete() && selectedVersions != null;
+ }
+
+ public VersionRange getSelectedVersions() {
+ return selectedVersions;
+ }
+
+ public void setVersions(VersionRange versions) {
+ selectedVersions = versions;
+ if(pnlVersionRange != null && !pnlVersionRange.isDisposed()) {
+ pnlVersionRange.setVersions(versions);
+ updateButtons();
+ }
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
new file mode 100644
index 0000000..a948427
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+/**
+ * @author dave
+ *
+ */
+public class OverviewForm extends SigilPage {
+ public static final String PAGE_ID = "overview";
+ private ISigilProjectModel sigil;
+
+ public OverviewForm(SigilProjectEditorPart editor, ISigilProjectModel sigil) {
+ super(editor, PAGE_ID, "Overview");
+ this.sigil = sigil;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Overview" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ try {
+ GeneralInfoSection general = new GeneralInfoSection(this, left, sigil);
+ managedForm.addPart( general );
+
+ ContentSummarySection content = new ContentSummarySection( this, right, sigil);
+ managedForm.addPart( content );
+
+ // XXX-FELIX
+ // commented out due to removal of runtime newton integration
+ // potential to bring back in medium term...
+ //TestingSection testing = new TestingSection(this, right, newton);
+ //managedForm.addPart(testing);
+
+ ToolsSection tools = new ToolsSection(this, right, sigil);
+ managedForm.addPart(tools);
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create overview form", e );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
new file mode 100644
index 0000000..80c2328
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class PackageExportDialog extends ResourceSelectDialog {
+
+ private Text versionText;
+ private Version version;
+
+ public PackageExportDialog(Shell parentShell, String title, IContentProvider content, ViewerFilter filter, Object scope ) {
+ super(parentShell, content, filter, scope, title, "Package Name:", true);
+ }
+
+ @Override
+ protected void createCustom(Composite body) {
+ Label l = new Label( body, SWT.NONE );
+ l.setText( "Version:" );
+ versionText = new Text(body, SWT.BORDER);
+ versionText.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ try {
+ version = Version.parseVersion(versionText.getText());
+ setErrorMessage(null);
+ }
+ catch (IllegalArgumentException ex) {
+ setErrorMessage("Invalid version");
+ }
+ }
+ });
+ if ( version != null ) {
+ versionText.setText( version.toString() );
+ }
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
new file mode 100644
index 0000000..ca745f5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.InputStream;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Widget;
+
+public class ProjectLabelProvider extends DefaultLabelProvider implements IBaseLabelProvider {
+
+ private final Widget parent;
+ private final ImageRegistry registry;
+ private final IDependencyChecker checker;
+
+ public ProjectLabelProvider(Widget parent, ImageRegistry registry ) {
+ this(parent, registry, null);
+ }
+
+ public ProjectLabelProvider(Widget parent, ImageRegistry registry, IDependencyChecker checker) {
+ this.parent = parent;
+ this.registry = registry;
+ this.checker = checker;
+ }
+
+ public Image getImage(Object element) {
+ Image result = null;
+ if ( element instanceof ISigilBundle || element instanceof IRequiredBundle || element instanceof IBundleModelElement) {
+ result = findBundle();
+ }
+ else if (element instanceof IPackageImport) {
+ if(checker != null) {
+ result = checker.isSatisfied((IPackageImport) element) ? findPackage() : findPackageError();
+ } else {
+ result = findPackage();
+ }
+ }
+ else if (element instanceof IPackageExport) {
+ result = findPackage();
+ }
+ else if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+ try {
+ if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+ result = findPackage();
+ }
+ else {
+ result = findBundle();
+ }
+ } catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect package fragment root", e );
+ }
+ }
+ else if ( element instanceof IClasspathEntry ) {
+ result = findPackage();
+ }
+
+ return result;
+ }
+
+ public String getText(Object element) {
+ if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ return bundle.getBundleInfo().getSymbolicName();
+ }
+
+ if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle req = (IRequiredBundle) element;
+ return req.getSymbolicName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageImport ) {
+ IPackageImport req = (IPackageImport) element;
+ return req.getPackageName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) element;
+ return pe.getPackageName() + " " + pe.getVersion();
+ }
+
+ if ( element instanceof IResource ) {
+ IResource resource = (IResource) element;
+ return resource.getName();
+ }
+
+ if ( element instanceof IPackageFragment ) {
+ IPackageFragment f = (IPackageFragment) element;
+ return f.getElementName();
+ }
+
+ if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+ try {
+ return f.getUnderlyingResource().getName();
+ } catch (JavaModelException e) {
+ return "unknown";
+ }
+ }
+
+ if ( element instanceof IClasspathEntry ) {
+ IClasspathEntry cp = (IClasspathEntry) element;
+ return cp.getPath().toString();
+ }
+
+ return element.toString();
+ }
+
+ private Image findPackage() {
+ Image image = registry.get( "package" );
+
+ if ( image == null ) {
+ image = loadImage( "icons/package_obj.png" );
+ registry.put( "package", image);
+ }
+
+ return image;
+ }
+
+ private Image findPackageError() {
+ Image image = registry.get("package_error");
+ if(image == null) {
+ image = loadImage("icons/package_obj_error.gif");
+ registry.put("package_error", image);
+ }
+ return image;
+ }
+
+ private Image findBundle() {
+ Image image = registry.get( "bundle" );
+
+ if ( image == null ) {
+ image = loadImage( "icons/jar_obj.png" );
+ registry.put( "bundle", image);
+ }
+
+ return image;
+ }
+
+ private Image loadImage(String resource) {
+ InputStream in = ProjectLabelProvider.class.getClassLoader().getResourceAsStream( resource );
+ ImageData data = new ImageData( in );
+ return new Image( parent.getDisplay(), data );
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
new file mode 100644
index 0000000..362de5b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.widgets.Table;
+
+public class ProjectTableViewer extends TableViewer {
+
+ private ModelLabelProvider labelProvider;
+
+ public ProjectTableViewer(Table table) {
+ super(table);
+ labelProvider = new ModelLabelProvider();
+ setLabelProvider(labelProvider);
+ }
+
+ @Override
+ public void setContentProvider(IContentProvider provider) {
+ super.setContentProvider(provider);
+ setInput(getTable());
+ }
+
+ public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+ labelProvider.setUnresolvedElements(elements);
+ }
+
+ @Override
+ public ModelLabelProvider getLabelProvider() {
+ return labelProvider;
+ }
+
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
new file mode 100644
index 0000000..1e069fd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.IPropertiesFilePartitions;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.PropertiesFileSourceViewerConfiguration;
+import org.eclipse.jdt.ui.text.JavaTextTools;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+@SuppressWarnings("restriction")
+public class PropertiesForm extends SigilSourcePage {
+
+ public static final String PAGE_ID = "properties";
+
+ public PropertiesForm(SigilProjectEditorPart editor, ISigilProjectModel project) {
+ super(PAGE_ID);
+ JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
+ IPreferenceStore store= JavaPlugin.getDefault().getCombinedPreferenceStore();
+ setPreferenceStore(store);
+ setSourceViewerConfiguration(new PropertiesFileSourceViewerConfiguration(textTools.getColorManager(), store, this, IPropertiesFilePartitions.PROPERTIES_FILE_PARTITIONING));
+ /*IFileEditorInput fileInput = (IFileEditorInput) editor.getEditorInput();
+ this.setDocumentProvider(fileInput);*/
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
new file mode 100644
index 0000000..5adf84d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class RequiresBundleSection extends BundleDependencySection {
+
+ public RequiresBundleSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+ super( page, parent, project, unresolvedElements );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Requires Bundles";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which bundles this bundle depends on." );
+ }
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return getBundle().getBundleInfo().getRequiredBundles().toArray();
+ }
+ };
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ @Override
+ protected void handleAdd() {
+ try {
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+
+ if (dialog.open() == Window.OK) {
+ IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+ required.setSymbolicName(dialog.getSelectedName());
+ required.setVersions(dialog.getSelectedVersions());
+ required.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().addRequiredBundle(required);
+ refresh();
+ markDirty();
+ }
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to build required bundle", e );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {
+ IRequiredBundle requiredBundle = i.next();
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog(getSection().getShell(), "Edit Imported Package", getProjectModel(), requiredBundle, getBundle().getBundleInfo().getRequiredBundles() );
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ requiredBundle.setSymbolicName(dialog.getSelectedName());
+ requiredBundle.setVersions(dialog.getSelectedVersions());
+ requiredBundle.setOptional(dialog.isOptional());
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeRequiredBundle( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
new file mode 100644
index 0000000..efd61d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class ResourceBuildSection extends AbstractResourceSection implements ICheckStateListener, IResourceChangeListener, IPropertyChangeListener {
+
+ private ExcludedResourcesFilter resourcesFilter;
+
+ /**
+ * @param page
+ * @param parent
+ * @param project
+ * @throws CoreException
+ */
+ public ResourceBuildSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Resources" );
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Specify which resources are included in the bundle." );
+
+ tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+ Link link = new Link(body, SWT.WRAP);
+ link.setText("Some resources may be filtered according to preferences. <a href=\"excludedResourcePrefs\">Click here</a> to edit the list of exclusions.");
+
+ TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+ data.heightHint = 200;
+ tree.setLayoutData( data );
+
+ viewer = new CheckboxTreeViewer( tree );
+ IProject base = getProjectModel().getProject();
+ viewer.setContentProvider( new ContainerTreeProvider() );
+ viewer.setLabelProvider( new ModelLabelProvider() );
+ viewer.addCheckStateListener( this );
+ resourcesFilter = new ExcludedResourcesFilter();
+ viewer.addFilter(resourcesFilter);
+ viewer.setInput(base);
+
+ link.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ if("excludedResourcePrefs".equals(event.text)) {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(getPage().getEditorSite().getShell(), SigilCore.EXCLUDED_RESOURCES_PREFERENCES_ID, null, null);
+ dialog.open();
+ }
+ }
+ });
+
+ SigilCore.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+
+ startWorkspaceListener(base.getWorkspace());
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ ISigilBundle bundle = getProjectModel().getBundle();
+
+ bundle.clearSourcePaths();
+
+ SigilUI.runInUISync( new Runnable() {
+ public void run() {
+ for ( Object o : viewer.getCheckedElements() ) {
+ if ( !viewer.getGrayed(o) ) {
+ IResource r = (IResource) o;
+ getProjectModel().getBundle().addSourcePath( r.getProjectRelativePath() );
+ }
+ }
+ }
+ });
+
+ super.commit(onSave);
+ }
+
+ @Override
+ protected void refreshSelections() {
+ // zero the state
+ for ( IPath path : getProjectModel().getBundle().getSourcePaths() ) {
+ IResource r = findResource( path );
+ if ( r != null ) {
+ viewer.expandToLevel(r, 0);
+ viewer.setChecked( r, true );
+ viewer.setGrayed( r, false );
+ handleStateChanged(r, true, false, false);
+ }
+ else {
+ SigilCore.error( "Unknown path " + path );
+ }
+ }
+ }
+
+ @Override
+ protected void syncResourceModel(IResource element, boolean checked) {
+ if ( checked ) {
+ getProjectModel().getBundle().addSourcePath( element.getProjectRelativePath() );
+ }
+ else {
+ getProjectModel().getBundle().removeSourcePath( element.getProjectRelativePath() );
+ }
+
+ markDirty();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ SigilCore.getDefault().getPreferenceStore().removePropertyChangeListener(this);
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ resourcesFilter.loadExclusions();
+ viewer.refresh();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
new file mode 100644
index 0000000..e50fb21
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+public class ResourceImportDialog extends ResourceSelectDialog implements VersionsChangeListener {
+
+ private VersionRangeComponent versions;
+ private VersionRange range;
+
+ public ResourceImportDialog(Shell parentShell, String title, String label, IContentProvider content, ViewerFilter filter, Object scope ) {
+ super(parentShell, content, filter, scope, title, label, true);
+ }
+
+ public VersionRange getVersions() {
+ return range;
+ }
+
+ @Override
+ protected void createCustom(Composite body) {
+ versions = new VersionRangeComponent(body, SWT.BORDER );
+ versions.addVersionChangeListener(this);
+ versions.setVersions(range);
+
+ GridData data = new GridData( SWT.LEFT, SWT.TOP, true, true );
+ data.horizontalSpan = 2;
+ data.widthHint = 300;
+ data.heightHint = 200;
+ versions.setLayoutData(data);
+ }
+
+ @Override
+ protected void selectionChanged(SelectionChangedEvent event) {
+ if ( event.getSelection().isEmpty() ) {
+ versions.setEnabled(false);
+ }
+ else {
+ versions.setEnabled(true);
+ }
+ }
+
+ public void versionsChanged(VersionRange range) {
+ this.range = range;
+ if ( range == null ) {
+ setErrorMessage( "Invalid version" );
+ }
+ else {
+ setErrorMessage( null );
+ }
+ }
+
+ public void setVersions(VersionRange range) {
+ this.range = range;
+ }
+
+ public IPackageImport getImport() {
+ IPackageImport packageImport = ModelElementFactory.getInstance().newModelElement( IPackageImport.class );
+ packageImport.setPackageName( (String) getSelected()[0] );
+ packageImport.setVersions( getVersions() );
+ return packageImport;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
new file mode 100644
index 0000000..e967548
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.cauldron.sigil.ui.util.SingletonSelection;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.StringConverter;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+
+public class ResourceSelectDialog extends Dialog {
+
+ private AtomicInteger keyCheck = new AtomicInteger();
+ private ScheduledExecutorService background = Executors.newSingleThreadScheduledExecutor();
+
+ private class UpdateViewerRunnable implements Runnable {
+ private int check;
+ public UpdateViewerRunnable(int check) {
+ this.check = check;
+ }
+
+ public void run() {
+ if ( check == keyCheck.get() ) {
+ try {
+ viewer.refresh();
+ }
+ catch (SWTException e) {
+ // discard
+ }
+ }
+ }
+ }
+
+ private static final ISelection EMPTY_SELECTION = new ISelection() {
+ public boolean isEmpty() {
+ return true;
+ }
+ };
+
+ private Job job;
+
+ private boolean isCombo;
+ private String title;
+ private String selectionText;
+
+ private StructuredViewer viewer;
+ private Combo resourceNameCombo;
+ private Table resourceNameTable;
+ private Text errorMessageText;
+ private String errorMessage;
+
+ private ViewerFilter filter;
+ private Object[] selected;
+
+ private Object scope;
+ private IContentProvider content;
+ private ILabelProvider labelProvider;
+
+ public ResourceSelectDialog(Shell parentShell, IContentProvider content, ViewerFilter filter, Object scope, String title, String selectionText, boolean isCombo) {
+ super(parentShell);
+ this.title = title;
+ this.selectionText = selectionText;
+ this.content = content;
+ this.filter = filter;
+ this.scope = scope;
+ this.isCombo = isCombo;
+ }
+
+ public void setJob(Job job) {
+ this.job = job;
+ }
+
+ public void refreshResources() {
+ try {
+ getShell().getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ try {
+ viewer.refresh();
+ }
+ catch (SWTException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ }
+ });
+ }
+ catch (NullPointerException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ catch (SWTException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ if (title != null) {
+ shell.setText(title);
+ }
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ if ( getItemCount() == 0 ) {
+ setErrorMessage( "No resources available" );
+ getButton(IDialogConstants.OK_ID).setEnabled(false);
+ }
+ else {
+ ISelection selection = selected == null ? EMPTY_SELECTION : new SingletonSelection( selected );
+ setSelected( new SelectionChangedEvent( viewer, selection ), true );
+ }
+
+ if ( job != null ) {
+ job.schedule();
+ }
+ }
+
+ @Override
+ public boolean close() {
+ if ( job != null ) {
+ job.cancel();
+ }
+ background.shutdownNow();
+ return super.close();
+ }
+
+ private int getItemCount() {
+ if ( isCombo ) {
+ return resourceNameCombo.getItemCount();
+ }
+ else {
+ return resourceNameTable.getItemCount();
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite body = (Composite) super.createDialogArea(parent);
+
+ GridLayout layout = (GridLayout) body.getLayout();
+ layout.numColumns = 2;
+ GridData data;
+
+ labelProvider = new ModelLabelProvider();
+
+ Label l = new Label( body, SWT.LEFT );
+ l.setText( selectionText );
+ data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ if ( !isCombo ) {
+ data.horizontalSpan = 2;
+ }
+ l.setLayoutData( data );
+
+ if ( isCombo ) {
+ createCombo( body );
+ }
+ else {
+ createTable( body );
+ }
+
+ viewer.addFilter( filter );
+ viewer.setContentProvider(content);
+ viewer.setLabelProvider( getLabelProvider() );
+ viewer.setComparator( new ViewerComparator() );
+ viewer.setInput( scope );
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setSelected(event, false);
+ }
+ });
+ createCustom( body );
+
+ errorMessageText = new Text(body, SWT.READ_ONLY | SWT.WRAP);
+ data = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
+ data.horizontalSpan = 2;
+ errorMessageText.setLayoutData(data);
+ errorMessageText.setBackground(errorMessageText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+ setErrorMessage(errorMessage);
+
+ return body;
+ }
+
+ protected void createCustom(Composite body) {
+ }
+
+ private void createCombo(Composite body) {
+ resourceNameCombo = new Combo( body, SWT.SINGLE | SWT.BORDER );
+ GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.widthHint = 200;
+ resourceNameCombo.setLayoutData( data );
+
+ viewer = new ComboViewer(resourceNameCombo);
+ }
+
+ private void createTable(Composite body) {
+ final Text txtFilter = new Text(body, SWT.BORDER);
+ GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.horizontalSpan = 2;
+ data.widthHint = 400;
+ txtFilter.setLayoutData(data);
+
+ resourceNameTable = new Table( body, SWT.MULTI | SWT.BORDER );
+ data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.widthHint = 400;
+ data.heightHint = 400;
+ resourceNameTable.setLayoutData( data );
+
+ viewer = new TableViewer(resourceNameTable);
+
+ txtFilter.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ switch ( e.keyCode ) {
+ case SWT.ARROW_UP:
+ scrollTable(-1);
+ break;
+ case SWT.ARROW_DOWN:
+ scrollTable(+1);
+ break;
+ default:
+ Runnable r = new UpdateViewerRunnable(keyCheck.incrementAndGet());
+ background.schedule(r, 100, TimeUnit.MILLISECONDS);
+ break;
+ }
+ }
+ });
+
+ ViewerFilter filter = new ViewerFilter() {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement,
+ Object element) {
+ return getLabelProvider().getText(element).startsWith( txtFilter.getText() );
+ }
+ };
+
+ viewer.addFilter(filter);
+ }
+
+ private void scrollTable(int delta) {
+ int i = resourceNameTable.getSelectionIndex();
+
+ if ( i == -1 ) {
+ if ( delta < 0 ) {
+ i = resourceNameTable.getItemCount() - 1;
+ }
+ else {
+ i = 0;
+ }
+ }
+ else {
+ i+=delta;
+ }
+
+ if ( i > -1 && i < resourceNameTable.getItemCount() ) {
+ Item item = resourceNameTable.getItem(i);
+ resourceNameTable.select(i);
+ selected = new Object[] { item.getData() };
+ ISelection selection = new SingletonSelection( selected );
+ selectionChanged(new SelectionChangedEvent(viewer, selection));
+ viewer.reveal(selected);
+ }
+ }
+
+ private void setSelected(SelectionChangedEvent event, boolean reveal) {
+ if ( event.getSelection().isEmpty() ) {
+ selected = null;
+ setErrorMessage( "No resource selected" );
+ }
+ else {
+ selected = ((IStructuredSelection) event.getSelection()).toArray();
+ setErrorMessage( null );
+ }
+
+ selectionChanged(event);
+
+ if ( reveal && !event.getSelection().isEmpty() ) {
+ if ( resourceNameCombo != null ) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ resourceNameCombo.select(resourceNameCombo.indexOf((String) sel.getFirstElement()));
+ }
+ else {
+ viewer.setSelection(event.getSelection(), true);
+ }
+ }
+ }
+
+ protected ILabelProvider getLabelProvider() {
+ return labelProvider;
+ }
+
+ public Object[] getSelected() {
+ return selected;
+ }
+
+ public void setSelected(Object[] selected) {
+ this.selected = selected;
+ }
+
+ protected void selectionChanged(SelectionChangedEvent event) {
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ if (errorMessageText != null && !errorMessageText.isDisposed()) {
+ errorMessageText.setText(errorMessage == null ? " \n " : errorMessage);
+ boolean hasError = errorMessage != null && (StringConverter.removeWhiteSpaces(errorMessage)).length() > 0;
+ errorMessageText.setEnabled(hasError);
+ errorMessageText.setVisible(hasError);
+ errorMessageText.getParent().update();
+ Control ok = getButton(IDialogConstants.OK_ID);
+ if (ok != null) {
+ ok.setEnabled(!hasError);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
new file mode 100644
index 0000000..c1fafaf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+
+public class SigilProjectEditorPart extends FormEditor implements IResourceChangeListener {
+
+ private final Set<IModelElement> unresolvedElements = Collections.synchronizedSet(new HashSet<IModelElement>());
+ private ISigilProjectModel project;
+ private volatile boolean saving = false;
+ private int dependenciesPageIndex;
+
+ // XXX-FIXME-XXX
+ private Image errorImage = null; //SigilUI.imageDescriptorFromPlugin(SigilUI.PLUGIN_ID, "icons/error.gif").createImage();
+ private PropertiesForm textPage;
+
+ public IProject getProject() {
+ IFileEditorInput fileInput = (IFileEditorInput) getEditorInput();
+ return fileInput.getFile().getProject();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ monitor.beginTask("Saving", IProgressMonitor.UNKNOWN);
+ try {
+ saving = true;
+ new ProgressMonitorDialog(getSite().getShell()).run(true, true, new IRunnableWithProgress() {
+ public void run(final IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ try {
+ if ( textPage.isDirty() ) {
+ SigilUI.runInUISync(new Runnable() {
+ public void run() {
+ textPage.doSave(monitor);
+ }
+ });
+ project.setBundle(null);
+ }
+ else if ( isDirty() ) {
+ commitPages(true);
+ project.save(monitor);
+ SigilUI.runInUISync(new Runnable() {
+ public void run() {
+ editorDirtyStateChanged();
+ }
+ });
+ }
+
+ monitor.done();
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ });
+ } catch (InvocationTargetException e) {
+ SigilCore.error("Failed to save " + project, e.getTargetException());
+ } catch (InterruptedException e) {
+ monitor.setCanceled(true);
+ return;
+ } finally {
+ saving = false;
+ }
+ monitor.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
+ */
+ @Override
+ protected void addPages() {
+ try {
+ addPage(new OverviewForm(this, project));
+ addPage(new ContentsForm(this, project));
+ dependenciesPageIndex = addPage(new DependenciesForm(this, project, unresolvedElements));
+ addPage(new ExportsForm(this, project));
+ textPage = new PropertiesForm(this, project);
+ addPage(textPage, getEditorInput());
+ setPartName(project.getSymbolicName());
+
+ refreshTabImages();
+ }
+ catch (PartInitException e) {
+ SigilCore.error( "Failed to build " + this, e );
+ }
+ }
+
+ protected void refreshTabImages() {
+ if(unresolvedElements.isEmpty()) {
+ setPageImage(dependenciesPageIndex, null);
+ } else {
+ setPageImage(dependenciesPageIndex, errorImage);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#doSaveAs()
+ */
+ @Override
+ public void doSaveAs() {
+ // save as not allowed
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
+ */
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ @Override
+ public void dispose() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ errorImage.dispose();
+ super.dispose();
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ IResourceDelta delta = event.getDelta();
+ final IFile editorFile = ((IFileEditorInput) getEditorInput()).getFile();
+ try {
+ delta.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ int kind = delta.getKind();
+ IResource resource = delta.getResource();
+ if(resource instanceof IProject) {
+ if(!editorFile.getProject().equals(resource)) {
+ return false;
+ }
+ if(kind == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.MARKERS) > 0) {
+ loadUnresolvedDependencies();
+ refreshAllPages();
+ }
+ return true;
+ }
+
+ if(resource instanceof IFile) {
+ IFile affectedFile = (IFile) resource;
+ if(affectedFile.equals(editorFile)) {
+ switch (kind) {
+ case IResourceDelta.REMOVED:
+ close(false);
+ break;
+ case IResourceDelta.CHANGED:
+ if(!saving) {
+ reload();
+ }
+ SigilUI.runInUISync( new Runnable() {
+ public void run() {
+ setPartName(project.getSymbolicName());
+ }
+ } );
+ break;
+ }
+ }
+ // Recurse no more
+ return false;
+ }
+
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ ErrorDialog.openError(getSite().getShell(), "Error", null, e.getStatus());
+ }
+ }
+
+ protected void refreshAllPages() {
+ Runnable op = new Runnable() {
+ public void run() {
+ for(Iterator<?> iter = pages.iterator(); iter.hasNext(); ) {
+ IFormPage page = (IFormPage) iter.next();
+ if(page != null) {
+ IManagedForm managedForm = page.getManagedForm();
+ if(managedForm != null) {
+ managedForm.refresh();
+ IFormPart[] parts = managedForm.getParts();
+ for (IFormPart part : parts) {
+ part.refresh();
+ }
+ }
+ }
+ }
+ firePropertyChange(IEditorPart.PROP_DIRTY);
+ setPartName(project.getSymbolicName());
+ refreshTabImages();
+ }
+ };
+ getSite().getShell().getDisplay().syncExec(op);
+ }
+
+ protected void reload() {
+ project.setBundle(null);
+ refreshAllPages();
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ super.init(site, input);
+
+ try {
+ this.project = SigilCore.create(getProject());
+ } catch (CoreException e) {
+ throw new PartInitException("Error creating Sigil project", e);
+ }
+
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+
+ if(input instanceof IFileEditorInput) {
+ try {
+ loadUnresolvedDependencies();
+ } catch (CoreException e) {
+ throw new PartInitException("Error retrieving dependency markers", e);
+ }
+ }
+ }
+
+ private void loadUnresolvedDependencies() throws CoreException {
+ ModelElementFactory factory = ModelElementFactory.getInstance();
+ IMarker[] markers = getProject().findMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE);
+ unresolvedElements.clear();
+
+ for (IMarker marker : markers) {
+ String elementName = (String) marker.getAttribute("element");
+ String versionRangeStr = (String) marker.getAttribute("versionRange");
+ if(elementName != null && versionRangeStr != null) {
+ if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE)) {
+ IPackageImport pkgImport = factory.newModelElement(IPackageImport.class);
+ pkgImport.setPackageName(elementName);
+ pkgImport.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+ unresolvedElements.add(pkgImport);
+ } else if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE)) {
+ IRequiredBundle req = factory.newModelElement(IRequiredBundle.class);
+ req.setSymbolicName(elementName);
+ req.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+ unresolvedElements.add(req);
+ }
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
new file mode 100644
index 0000000..291c927
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+import org.eclipse.ui.ide.IDE;
+
+public class SigilSourcePage extends TextEditor implements IFormPage {
+ private final String id;
+ private int index;
+ private SigilProjectEditorPart editor;
+ private boolean active;
+ private Control control;
+
+ public SigilSourcePage(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
+ Control[] children = parent.getChildren();
+ control = children[children.length - 1];
+ getSourceViewer().addTextListener( new ITextListener() {
+ public void textChanged(TextEvent event) {
+ if ( editor != null ) {
+ editor.refreshAllPages();
+ }
+ }
+ });
+ //PlatformUI.getWorkbench().getHelpSystem().setHelp(fControl, IHelpContextIds.MANIFEST_SOURCE_PAGE);
+ }
+
+ public void initialize(FormEditor editor) {
+ this.editor = (SigilProjectEditorPart) editor;
+ }
+
+ public FormEditor getEditor() {
+ return editor;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public Control getPartControl() {
+ return control;
+ }
+
+ public boolean selectReveal(Object object) {
+ if (object instanceof IMarker) {
+ IDE.gotoMarker(this, (IMarker) object);
+ return true;
+ }
+ return false;
+ }
+
+ // static impls
+ public boolean isEditor() {
+ return true;
+ }
+
+ public boolean canLeaveThePage() {
+ return true;
+ }
+
+ public IManagedForm getManagedForm() {
+ // this is not a form
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
new file mode 100644
index 0000000..ca0eb01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class TestingSection extends SigilSection {
+
+ public TestingSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("Testing");
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Test this project by launching a separate Newton application:" );
+
+ Hyperlink launch = toolkit.createHyperlink( body, "Launch a newton container", SWT.NULL );
+ launch.setHref( "launchShortcut.run.org.cauldron.sigil.launching.shortcut" );
+ launch.addHyperlinkListener(this);
+
+ Hyperlink debug = toolkit.createHyperlink( body, "Debug a newton container", SWT.NULL );
+ debug.setHref( "launchShortcut.debug.org.cauldron.sigil.launching.shortcut" );
+ debug.addHyperlinkListener(this);
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ String href = (String) e.getHref();
+ if (href.startsWith("launchShortcut.")) { //$NON-NLS-1$
+ href = href.substring(15);
+ int index = href.indexOf('.');
+ if (index < 0)
+ return; // error. Format of href should be launchShortcut.<mode>.<launchShortcutId>
+ String mode = href.substring(0, index);
+ String id = href.substring(index + 1);
+
+ //getEditor().doSave(null);
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor("org.eclipse.debug.ui.launchShortcuts"); //$NON-NLS-1$
+ for (int i = 0; i < elements.length; i++) {
+ if (id.equals(elements[i].getAttribute("id"))) //$NON-NLS-1$
+ try {
+ ILaunchShortcut shortcut = (ILaunchShortcut)elements[i].createExecutableExtension("class"); //$NON-NLS-1$
+ // preLaunch();
+ shortcut.launch(new StructuredSelection(getLaunchObject()), mode);
+ } catch (CoreException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private Object getLaunchObject() {
+ return getProjectModel().getProject();
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
new file mode 100644
index 0000000..22d9824
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.actions.PruneProjectDependenciesAction;
+import org.cauldron.sigil.actions.ResolveProjectDependenciesAction;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ToolsSection extends SigilSection {
+
+ public ToolsSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("Tools");
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Tools to help manage this project:" );
+
+ Hyperlink launch = toolkit.createHyperlink( body, "Resolve missing dependencies", SWT.NULL );
+ launch.setHref( "resolve" );
+ launch.addHyperlinkListener(this);
+
+ Hyperlink debug = toolkit.createHyperlink( body, "Prune unused dependencies", SWT.NULL );
+ debug.setHref( "prune" );
+ debug.addHyperlinkListener(this);
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ String href = (String) e.getHref();
+ if ( "resolve".equals( href ) ) {
+ handleResolve();
+ }
+ else if ( "prune".equals( href ) ) {
+ handlePrune();
+ }
+ }
+
+ private void handlePrune() {
+ new PruneProjectDependenciesAction(getProjectModel()).run();
+ }
+
+ private void handleResolve() {
+ final ISigilProjectModel project = getProjectModel();
+ new ResolveProjectDependenciesAction(project, true).run();
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
new file mode 100644
index 0000000..7591b38
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class VersionRangeComponent extends Composite {
+ private VersionRange versions = VersionRange.ANY_VERSION;
+
+ private Button specificButton;
+
+ private Text specificText;
+ private Button rangeButton;
+ private Text minimumText;
+ private Text maximumText;
+ private Button minInclusiveButton;
+ private Button maxInclusiveButton;
+
+ private Set<VersionsChangeListener> listeners = new HashSet<VersionsChangeListener>();
+ private Set<IValidationListener> validationListeners = new HashSet<IValidationListener>();
+
+ public VersionRangeComponent(Composite parent, int style) {
+ super( parent, style );
+ createComponents(this);
+ }
+
+ public void addVersionChangeListener(VersionsChangeListener listener) {
+ synchronized(listeners) {
+ listeners.add( listener );
+ }
+ }
+
+ public void removeVersionChangeListener(VersionsChangeListener listener) {
+ synchronized(listeners) {
+ listeners.remove( listener );
+ }
+ }
+
+ public void addValidationListener(IValidationListener listener) {
+ validationListeners.add(listener);
+ }
+
+ public void removeValidationListener(IValidationListener listener) {
+ validationListeners.remove(listener);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ specificButton.setEnabled(enabled);
+ rangeButton.setEnabled(enabled);
+ if ( enabled ) {
+ specificButton.setSelection(versions.isPointVersion());
+ setSpecific();
+ }
+ else {
+ minimumText.setEnabled(enabled);
+ maximumText.setEnabled(enabled);
+ minInclusiveButton.setEnabled(enabled);
+ maxInclusiveButton.setEnabled(enabled);
+ }
+ }
+
+
+ public VersionRange getVersions() {
+ return versions;
+ }
+
+ public void setVersions(VersionRange versions) {
+ this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+ updateFields();
+ }
+
+ private void updateFields() {
+ if ( versions.isPointVersion() ) {
+ specificButton.setSelection(true);
+ specificText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getFloor().toString() );
+ }
+ else {
+ rangeButton.setSelection( true );
+ minimumText.setText( versions.getFloor().toString() );
+ minInclusiveButton.setSelection( !versions.isOpenFloor() );
+ maximumText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getCeiling().toString() );
+ maxInclusiveButton.setSelection( !versions.isOpenCeiling() );
+ }
+
+ setSpecific();
+ }
+
+ private void createComponents(Composite body) {
+ setLayout( new GridLayout( 3, false ) );
+
+ specificButton = new Button( body, SWT.RADIO );
+ specificButton.setText( "Specific:" );
+ specificButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setSpecific();
+ }
+ });
+
+ new Label( body, SWT.NONE ).setText("Version:");
+
+ specificText = new Text( body, SWT.BORDER );
+ specificText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ rangeButton = new Button( body, SWT.RADIO );
+ rangeButton.setText( "Range:" );
+
+ new Label(body, SWT.NONE).setText("Minimum:");
+
+ minimumText = new Text( body, SWT.BORDER );
+ minimumText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ minInclusiveButton = new Button( body, SWT.CHECK );
+ minInclusiveButton.setText( "inclusive" );
+ minInclusiveButton.setSelection(true);
+ minInclusiveButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setVersions();
+ }
+ });
+
+ new Label( body, SWT.NONE ).setText("Maximum:");
+ maximumText = new Text( body, SWT.BORDER );
+ maximumText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ maxInclusiveButton = new Button( body, SWT.CHECK );
+ maxInclusiveButton.setText( "inclusive" );
+ maxInclusiveButton.setSelection(false);
+ maxInclusiveButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setVersions();
+ }
+ });
+
+ // Layout
+ specificButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+ specificText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ rangeButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+ minimumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ maximumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ updateFields();
+ }
+
+ private void setVersions() {
+ try {
+ if ( specificButton.getSelection() ) {
+ if ( "*".equals( specificText.getText() ) ) {
+ versions = VersionRange.ANY_VERSION;
+ }
+ else if ( specificText.getText().trim().length() == 0 ) {
+ versions = null;
+ }
+ else {
+ Version v = Version.parseVersion( specificText.getText().trim() );
+ versions = new VersionRange( false, v, v, false);
+ }
+ }
+ else {
+ Version min = Version.parseVersion( minimumText.getText() );
+ Version max = "*".equals( maximumText.getText() ) ? VersionRange.INFINITE_VERSION : Version.parseVersion( maximumText.getText() );
+ versions = new VersionRange( !minInclusiveButton.getSelection(), min, max, !maxInclusiveButton.getSelection() );
+ }
+ fireValidationMessage(null, IMessageProvider.NONE);
+ }
+ catch (IllegalArgumentException e) {
+ versions = null;
+ fireValidationMessage("Invalid version", IMessageProvider.ERROR);
+ }
+
+ fireVersionChange();
+ }
+
+ private void fireVersionChange() {
+ synchronized( listeners ) {
+ for ( VersionsChangeListener l : listeners ) {
+ l.versionsChanged(versions);
+ }
+ }
+ }
+
+ private void fireValidationMessage(String message, int level) {
+ for (IValidationListener validationListener : validationListeners) {
+ validationListener.validationMessage(message, level);
+ }
+ }
+
+ private void setSpecific() {
+ boolean specific = specificButton.getSelection();
+ specificButton.setSelection(specific);
+ specificText.setEnabled(specific);
+ minimumText.setEnabled(!specific);
+ maximumText.setEnabled(!specific);
+ minInclusiveButton.setEnabled(!specific);
+ maxInclusiveButton.setEnabled(!specific);
+ setVersions();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
new file mode 100644
index 0000000..d97bdc2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface VersionsChangeListener {
+ void versionsChanged(VersionRange range);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
new file mode 100644
index 0000000..6f0e397
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+public class WrappedContentProposal<T> implements IContentProposal {
+
+ private final T element;
+ private final IElementDescriptor<? super T> descriptor;
+
+ private WrappedContentProposal(T element, IElementDescriptor<? super T> descriptor) {
+ this.element = element;
+ this.descriptor = descriptor;
+ }
+
+ public static <T> WrappedContentProposal<T> newInstance(T element, IElementDescriptor<? super T> descriptor) {
+ return new WrappedContentProposal<T>(element, descriptor);
+ }
+
+ public String getContent() {
+ return descriptor.getName(element);
+ }
+
+ public int getCursorPosition() {
+ return 0;
+ }
+
+ public String getDescription() {
+ return null;
+ }
+
+ public String getLabel() {
+ return descriptor.getLabel(element);
+ }
+
+ public T getElement() {
+ return element;
+ }
+
+ @Override
+ public String toString() {
+ return getLabel();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
new file mode 100644
index 0000000..34c8bb6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface IFormValueConverter {
+ String getLabel(Object value);
+ Object getValue(String label);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
new file mode 100644
index 0000000..89afefa
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface ISigilFormEntryListener {
+ void browseButtonSelected(SigilFormEntry form);
+ void textValueChanged(SigilFormEntry form);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
new file mode 100644
index 0000000..ea5bf9d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class SigilFormEntry {
+ private static final IFormValueConverter NULL_DESCRIPTOR = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ return (String) value;
+ }
+
+ public Object getValue(String label) {
+ return label;
+ }
+ };
+
+ private Label lbl;
+ private Text txt;
+ private Button btn;
+ private IFormValueConverter descriptor;
+ private boolean freeText = true;
+
+ private Object value;
+ private ISigilFormEntryListener listener;
+
+ public SigilFormEntry(Composite parent, FormToolkit toolkit, String title) {
+ this(parent, toolkit, title, null, null);
+ }
+
+ public SigilFormEntry(Composite parent, FormToolkit toolkit, String title, String browse, IFormValueConverter descriptor) {
+ this.descriptor = descriptor == null ? NULL_DESCRIPTOR : descriptor;
+ createComponent(parent, title, browse, toolkit);
+ }
+
+ public void setFormEntryListener(ISigilFormEntryListener listener) {
+ this.listener = listener;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ String text = descriptor.getLabel(value);
+ if ( text == null ) {
+ text = "";
+ }
+ txt.setText(text);
+ handleValueChanged();
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setFreeText(boolean freeText) {
+ this.freeText = freeText;
+ }
+
+ public void setEditable(boolean editable) {
+ lbl.setEnabled(editable);
+ txt.setEditable(editable);
+ if ( btn != null ) {
+ btn.setEnabled(editable);
+ }
+ }
+
+ private void createComponent(Composite parent, String title, String browse, FormToolkit toolkit) {
+ lbl = toolkit.createLabel(parent, title);
+ lbl.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ txt = toolkit.createText(parent, "", SWT.SINGLE | SWT.BORDER);
+ txt.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if ( freeText ) {
+ switch ( e.character ) {
+ case '\r': handleValueChanged();
+ }
+ }
+ else {
+ switch ( e.character ) {
+ case '\b':
+ setValue(null);
+ handleValueChanged();
+ default:
+ e.doit = false;
+ break;
+ }
+ }
+ }
+ });
+ txt.addFocusListener( new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ handleValueChanged();
+ }
+ });
+
+ if ( browse != null ) {
+ btn = toolkit.createButton(parent, browse, SWT.PUSH);
+ btn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleBrowseSelected();
+ }
+ });
+ }
+
+ fillIntoGrid(parent);
+ }
+
+ private void handleBrowseSelected() {
+ if ( listener != null ) {
+ listener.browseButtonSelected(this);
+ }
+ }
+
+ private void handleValueChanged() {
+ if ( freeText ) {
+ this.value = descriptor.getValue(txt.getText());
+ }
+ if ( listener != null ) {
+ listener.textValueChanged(this);
+ }
+ }
+
+ private void fillIntoGrid(Composite parent) {
+ if ( parent.getLayout() instanceof GridLayout ) {
+ GridLayout layout = (GridLayout) parent.getLayout();
+
+ int cols = layout.numColumns;
+
+ lbl.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, false, false) );
+
+ if ( btn == null ) {
+ txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 1), 1 ) );
+ }
+ else {
+ txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 2), 1 ) );
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
new file mode 100644
index 0000000..3170d01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public class SigilFormEntryAdapter implements ISigilFormEntryListener{
+ public void browseButtonSelected(SigilFormEntry form) {
+ }
+
+ public void textValueChanged(SigilFormEntry form) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
new file mode 100644
index 0000000..a2873ad
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+
+public class SigilPage extends FormPage {
+
+ public SigilPage(FormEditor editor, String id, String title) {
+ super(editor, id, title);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
new file mode 100644
index 0000000..b825ec0
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.pde.internal.ui.editor.FormLayoutFactory;
+import org.eclipse.pde.internal.ui.parts.FormEntry;
+import org.eclipse.pde.internal.ui.parts.IFormEntryListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IPartSelectionListener;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+@SuppressWarnings("restriction")
+public abstract class SigilSection extends SectionPart implements IFormEntryListener, IPartSelectionListener {
+
+ private SigilPage page;
+ private ISigilProjectModel project;
+
+ public SigilSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super(parent, page.getManagedForm().getToolkit(), ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED );
+ this.project = project;
+ this.page = page;
+ createSection( getSection(), page.getManagedForm().getToolkit() );
+ }
+
+ public ISigilProjectModel getProjectModel() {
+ return project;
+ }
+
+ public SigilPage getPage() {
+ return page;
+ }
+
+ public void setExpanded( boolean expanded ) {
+ getSection().setExpanded(expanded);
+ }
+
+ protected abstract void createSection(Section section,FormToolkit toolkit ) throws CoreException;
+
+ protected void setTitle( String title ) {
+ Section section = getSection();
+ section.setText( title );
+ section.setLayout(FormLayoutFactory.createClearTableWrapLayout(false, 1));
+ TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+ section.setLayoutData(data);
+ }
+
+ protected void setMarker(String type, String message, int priority, int severity) throws CoreException {
+ IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+ IMarker marker = file.getFile().createMarker(type);
+ marker.setAttribute( IMarker.MESSAGE, message );
+ marker.setAttribute(IMarker.PRIORITY, priority);
+ marker.setAttribute( IMarker.SEVERITY, severity );
+ }
+
+ protected void clearMarkers() throws CoreException {
+ IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+ file.getFile().deleteMarkers(null, true, IResource.DEPTH_INFINITE );
+ }
+
+ protected Composite createTableWrapBody( int columns, FormToolkit toolkit ) {
+ Section section = getSection();
+ Composite client = toolkit.createComposite(section);
+
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.leftMargin = layout.rightMargin = toolkit.getBorderStyle() != SWT.NULL ? 0 : 2;
+ layout.numColumns = columns;
+ client.setLayout(layout);
+ client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ section.setClient(client);
+
+ return client;
+ }
+
+ protected Composite createGridBody( int columns, boolean columnsSameWidth, FormToolkit toolkit ) {
+ Section section = getSection();
+ Composite client = toolkit.createComposite(section);
+
+ GridLayout layout = new GridLayout();
+
+ layout.makeColumnsEqualWidth = columnsSameWidth;
+ layout.numColumns = columns;
+ client.setLayout(layout);
+
+ client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ section.setClient(client);
+
+ return client;
+ }
+
+ public void browseButtonSelected(FormEntry entry) {
+ }
+
+ public void focusGained(FormEntry entry) {
+ }
+
+ public void selectionChanged(FormEntry entry) {
+ }
+
+ public void textDirty(FormEntry entry) {
+ }
+
+ public void textValueChanged(FormEntry entry) {
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+
+ public void selectionChanged(IFormPart part, ISelection selection) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
new file mode 100644
index 0000000..df5314b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class FileSystemRepositoryWizard extends RepositoryWizard {
+ @Override
+ public void addPages() {
+ addPage( new FileSystemRepositoryWizardPage(this) );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
new file mode 100644
index 0000000..a46f7d9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.io.File;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+
+public class FileSystemRepositoryWizardPage extends RepositoryWizardPage
+ implements IWizardPage {
+
+ private DirectoryFieldEditor dirEditor;
+
+ protected FileSystemRepositoryWizardPage(RepositoryWizard parent) {
+ super("File System Repository", parent);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ dirEditor = new DirectoryFieldEditor("dir", "Directory:", getFieldEditorParent() );
+ dirEditor.getTextControl( getFieldEditorParent() ).addModifyListener( new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ checkPageComplete();
+ }
+ } );
+
+ addField( dirEditor );
+ addField(new BooleanFieldEditor("recurse", "Recurse:", getFieldEditorParent() ) );
+ }
+
+ @Override
+ protected void checkPageComplete() {
+ super.checkPageComplete();
+ if ( isPageComplete() ) {
+ setPageComplete(dirEditor.getStringValue().length() > 0);
+ if ( isPageComplete() ) {
+ if ( new File( dirEditor.getStringValue()).isDirectory() ) {
+ setPageComplete(true);
+ setErrorMessage(null);
+ }
+ else {
+ setPageComplete(false);
+ setErrorMessage("Invalid directory");
+ }
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
new file mode 100644
index 0000000..ae8da79
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class OSGiInstallRepositoryWizard extends RepositoryWizard {
+ @Override
+ public void addPages() {
+ addPage( new OSGiInstallRepositoryWizardPage(this) );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
new file mode 100644
index 0000000..772b151
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+
+public class OSGiInstallRepositoryWizardPage extends RepositoryWizardPage {
+
+ protected OSGiInstallRepositoryWizardPage(RepositoryWizard parent) {
+ super("OSGi Install Repository", parent);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ ArrayList<String[]> installs = new ArrayList<String[]>();
+ for ( String id : SigilCore.getInstallManager().getInstallIDs() ) {
+ IOSGiInstall i = SigilCore.getInstallManager().findInstall(id);
+ installs.add( new String[] { i.getType().getName(), id } );
+ }
+ String[][] strs = installs.toArray( new String[installs.size()][] );
+
+ RadioGroupFieldEditor editor = new RadioGroupFieldEditor(
+ "id",
+ "Install",
+ 1, strs, getFieldEditorParent() );
+
+ addField(editor);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
new file mode 100644
index 0000000..34d428c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.perspective;
+
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.progress.IProgressConstants;
+
+public class SigilPerspectiveFactory implements IPerspectiveFactory {
+
+ private static final String ID_PROJECT_EXPLORER= "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
+ private static final String ID_SEARCH_VIEW = "org.eclipse.search.ui.views.SearchView"; //$NON-NLS-1$
+ private static final String ID_CONSOLE_VIEW = "org.eclipse.ui.console.ConsoleView"; //$NON-NLS-1$
+ private static final String ID_TEMPLATES_VIEW = "org.eclipse.ui.texteditor.TemplatesView"; //$NON-NLS-N$
+
+
+ public void createInitialLayout(IPageLayout layout) {
+ /*
+ * Use ProjectExplorer vs PackageExplorer due to a bug with Drag and Drop on Mac OS X which affects PackageExplorer
+ * but not ProjectExplorer. https://bugs.eclipse.org/bugs/show_bug.cgi?id=243529
+ */
+ String editorArea = layout.getEditorArea();
+
+ IFolderLayout folder= layout.createFolder("left", IPageLayout.LEFT, (float)0.25, editorArea); //$NON-NLS-1$
+ folder.addView(ID_PROJECT_EXPLORER);
+ folder.addView(JavaUI.ID_TYPE_HIERARCHY);
+ folder.addPlaceholder(IPageLayout.ID_RES_NAV);
+ folder.addPlaceholder(SigilUI.ID_REPOSITORY_VIEW);
+ folder.addView(IPageLayout.ID_OUTLINE);
+
+ IFolderLayout outputfolder= layout.createFolder("bottom", IPageLayout.BOTTOM, (float)0.75, editorArea); //$NON-NLS-1$
+ outputfolder.addView(IPageLayout.ID_PROBLEM_VIEW);
+ outputfolder.addView(JavaUI.ID_JAVADOC_VIEW);
+ outputfolder.addView(JavaUI.ID_SOURCE_VIEW);
+ outputfolder.addPlaceholder(ID_SEARCH_VIEW);
+ outputfolder.addPlaceholder(ID_CONSOLE_VIEW);
+ outputfolder.addPlaceholder(IPageLayout.ID_BOOKMARKS);
+ outputfolder.addPlaceholder(IProgressConstants.PROGRESS_VIEW_ID);
+ outputfolder.addPlaceholder(SigilUI.ID_DEPENDENCY_VIEW);
+ outputfolder.addPlaceholder(SigilUI.ID_INSTANCES_VIEW);
+
+ layout.addActionSet(IDebugUIConstants.LAUNCH_ACTION_SET);
+ layout.addActionSet(JavaUI.ID_ACTION_SET);
+ layout.addActionSet(JavaUI.ID_ELEMENT_CREATION_ACTION_SET);
+ layout.addActionSet(IPageLayout.ID_NAVIGATE_ACTION_SET);
+
+ // views - sigil
+ layout.addShowViewShortcut(SigilUI.ID_REPOSITORY_VIEW);
+ layout.addShowViewShortcut(SigilUI.ID_DEPENDENCY_VIEW);
+ layout.addShowViewShortcut(SigilUI.ID_INSTANCES_VIEW);
+
+ // views - java
+ layout.addShowViewShortcut(JavaUI.ID_PACKAGES);
+ layout.addShowViewShortcut(JavaUI.ID_TYPE_HIERARCHY);
+ layout.addShowViewShortcut(JavaUI.ID_SOURCE_VIEW);
+ layout.addShowViewShortcut(JavaUI.ID_JAVADOC_VIEW);
+
+ // views - search
+ layout.addShowViewShortcut(ID_SEARCH_VIEW);
+
+ // views - debugging
+ layout.addShowViewShortcut(ID_CONSOLE_VIEW);
+
+ // views - standard workbench
+ layout.addShowViewShortcut(IPageLayout.ID_OUTLINE);
+ layout.addShowViewShortcut(IPageLayout.ID_PROBLEM_VIEW);
+ layout.addShowViewShortcut(IPageLayout.ID_RES_NAV);
+ layout.addShowViewShortcut(IPageLayout.ID_TASK_LIST);
+ layout.addShowViewShortcut(IProgressConstants.PROGRESS_VIEW_ID);
+ layout.addShowViewShortcut(ID_PROJECT_EXPLORER);
+
+ // new actions - Java project creation wizard
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewPackageCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewClassCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewEnumCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.folder");//$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.file");//$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.editors.wizards.UntitledTextFileWizard");//$NON-NLS-1$
+
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newCompositeWizard");
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newSystemWizard");
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newProjectWizard");
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
new file mode 100644
index 0000000..134f3c3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.eclipse.core.internal.preferences.PrefsMessages;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class ExcludedResourcesPrefsPage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ private TableViewer viewer;
+ private IWorkbench workbench;
+ private ArrayList<String> resources;
+
+ public ExcludedResourcesPrefsPage() {
+ super();
+ setDescription("Specify resources that should not be offered for inclusion in a generated bundle.");
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ // Create controls
+ Composite composite = new Composite(parent, SWT.NONE);
+ Table table = new Table(composite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
+
+ Button btnAdd = new Button(composite, SWT.PUSH);
+ btnAdd.setText("Add");
+
+ final Button btnRemove = new Button(composite, SWT.PUSH);
+ btnRemove.setText("Remove");
+ btnRemove.setEnabled(false);
+
+ // Create viewer
+ viewer = new TableViewer(table);
+ viewer.setContentProvider(new ArrayContentProvider());
+
+ // Load data
+ loadPreferences(false);
+ viewer.setInput(resources);
+
+ // Listeners
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ btnRemove.setEnabled(!viewer.getSelection().isEmpty());
+ }
+ });
+
+ btnRemove.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+ Object[] deleted = selection.toArray();
+ for(Object delete : deleted) {
+ resources.remove(delete);
+ }
+ viewer.remove(deleted);
+ }
+ });
+
+ btnAdd.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ InputDialog dialog = new InputDialog(getShell(), "Add Resource", "Enter resource name", "", new IInputValidator() {
+ public String isValid(String newText) {
+ String error = null;
+ if(newText == null || newText.length() == 0) {
+ error = "Name must not be empty.";
+ } else if(resources.contains(newText)) {
+ error = "Specified resource name is already on the list.";
+ }
+ return error;
+ }
+ });
+
+ if(dialog.open() == Window.OK) {
+ String value = dialog.getValue();
+ resources.add(value);
+ viewer.add(value);
+ }
+ }
+ });
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+ btnAdd.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ btnRemove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+ return composite;
+ }
+
+ private void loadPreferences(boolean useDefaults) {
+ String resourcesListStr = useDefaults
+ ? getPreferenceStore().getDefaultString(SigilCore.DEFAULT_EXCLUDED_RESOURCES)
+ : getPreferenceStore().getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES);
+ String[] resourcesArray = PrefsUtils.stringToArray(resourcesListStr);
+
+ resources = new ArrayList<String>(resourcesArray.length);
+ for (String resource : resourcesArray) {
+ resources.add(resource);
+ }
+ }
+
+ public void init(IWorkbench workbench) {
+ this.workbench = workbench;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ public boolean performOk() {
+ String resourcesStr = PrefsUtils.arrayToString(resources.toArray(new String[resources.size()]));
+ getPreferenceStore().setValue(SigilCore.DEFAULT_EXCLUDED_RESOURCES, resourcesStr);
+ return true;
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ loadPreferences(true);
+ viewer.setInput(resources);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
new file mode 100644
index 0000000..d7b6786
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class LibraryConfigurationDialog extends TitleAreaDialog {
+
+ private static final Comparator<IPackageImport> COMPARATOR = new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo(o2.getPackageName());
+ }
+ };
+
+ private String name;
+ private Version version;
+ private TreeSet<IPackageImport> packageImports = new TreeSet<IPackageImport>(COMPARATOR);
+
+ private boolean editOnly;
+
+ private TableViewer viewer;
+ private Text txtName;
+ private Text txtVersion;
+
+ public LibraryConfigurationDialog(Shell parentShell) {
+ super(parentShell);
+ name = "";
+ version = Version.emptyVersion;
+ }
+
+ public LibraryConfigurationDialog(Shell parentShell, ILibrary lib) {
+ super(parentShell);
+ editOnly = true;
+ name = lib.getName();
+ version = lib.getVersion();
+ packageImports.addAll( lib.getImports() );
+ }
+
+ @Override
+ protected Control createDialogArea(Composite par) {
+ setTitle("Add Library");
+ Composite container = (Composite) super.createDialogArea(par);
+
+ Composite topPanel = new Composite(container, SWT.NONE);
+
+ new Label(topPanel, SWT.NONE).setText("Name");
+
+ txtName = new Text(topPanel, SWT.BORDER);
+ txtName.setEditable( !editOnly );
+ if(name != null) txtName.setText(name);
+
+ new Label(topPanel, SWT.NONE).setText("Version");
+
+ txtVersion = new Text(topPanel, SWT.BORDER);
+ txtVersion.setText( version.toString() );
+ txtVersion.setEditable( !editOnly );
+
+ Composite bottomPanel = new Composite(container, SWT.NONE);
+
+ Table table = new Table(bottomPanel, SWT.BORDER);
+ table.setSize( new Point( 300, 200 ) );
+
+ Button add = new Button(bottomPanel, SWT.PUSH);
+ add.setText("Add...");
+
+ final Button edit = new Button(bottomPanel, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(bottomPanel, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ updateState();
+
+ // Hookup Listeners
+ txtName.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateState();
+ }
+ });
+ txtVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateState();
+ }
+ });
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ });
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ });
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ });
+
+ // Layout
+ topPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ topPanel.setLayout(new GridLayout(2, false));
+ txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ bottomPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ bottomPanel.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+ // Table Viewer
+ viewer = new TableViewer(table);
+ viewer.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IPackageImport pi = (IPackageImport) element;
+ return pi.getPackageName() + " " + pi.getVersions();
+ }
+ });
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ edit.setEnabled(!event.getSelection().isEmpty());
+ remove.setEnabled(!event.getSelection().isEmpty());
+ }
+ });
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ viewer.setInput(packageImports);
+ return container;
+ }
+
+ private void updateState() {
+ String error = null;
+ String warning = null;
+
+ name = txtName.getText();
+
+ try {
+ version = new Version(txtVersion.getText());
+ if(version.getQualifier().indexOf('_') > -1) {
+ warning = "The use of underscores in a version qualifier is discouraged.";
+ }
+ } catch (IllegalArgumentException e) {
+ version = null;
+ error = "Invalid version format";
+ }
+
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ if(okButton != null && !okButton.isDisposed()) okButton.setEnabled(allowOkay());
+
+ setErrorMessage(error);
+ setMessage(warning, IMessageProvider.WARNING);
+ }
+
+ private boolean allowOkay() {
+ return name != null && name.length() > 0 && version != null;
+ }
+
+ @Override
+ protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
+ Button button = super.createButton(parent, id, label, defaultButton);
+ if(id == IDialogConstants.OK_ID) {
+ button.setEnabled(allowOkay());
+ }
+ return button;
+ }
+
+ private void handleAdd() {
+ /*NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog(getShell(), "Add Imported Package", null, packageImports);
+ if ( dialog.open() == Window.OK ) {
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName(dialog.getSelectedName());
+ pi.setVersions(dialog.getSelectedVersions());
+ pi.setOptional(dialog.isOptional());
+
+ packageImports.add(pi);
+ viewer.refresh();
+ }*/
+ }
+
+ private void handleEdit() {
+ /*IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageImport packageImport = i.next();
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog( getShell(), "Edit Imported Package", packageImport, packageImports );
+ dialog.setVersions( packageImport.getVersions() );
+ dialog.setOptional(packageImport.isOptional());
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ String packageName = dialog.getSelectedName();
+ VersionRange versionRange = dialog.getSelectedVersions();
+
+ IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ newImport.setPackageName(packageName);
+ newImport.setVersions(versionRange);
+ newImport.setOptional(dialog.isOptional());
+
+ packageImports.remove(packageImport);
+ packageImports.add(newImport);
+ }
+ }
+ }
+
+ if ( changed ) {
+ viewer.refresh();
+ } */
+ }
+
+ private void handleRemove() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ packageImports.remove( i.next() );
+ }
+
+ viewer.refresh();
+ }
+ }
+
+ public ILibrary getLibrary() {
+ ILibrary library = ModelElementFactory.getInstance().newModelElement(ILibrary.class);
+
+ library.setName(name);
+ library.setVersion(version);
+
+ for (IPackageImport pi : packageImports) {
+ library.addImport(pi);
+ }
+
+ return library;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
new file mode 100644
index 0000000..cf77287
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class LibraryPreferencePage extends PreferencePage implements
+ IWorkbenchPreferencePage {
+
+ private TreeSet<ILibrary> libraries;
+ private TableViewer libraryView;
+
+ private Table table;
+ private Button btnAdd;
+ private Button btnEdit;
+ private Button btnRemove;
+
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = initContents( parent );
+ loadPreferences();
+ return control;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ }
+
+ @Override
+ public boolean performOk() {
+ IPreferenceStore prefs = getPreferenceStore();
+ for ( String key : prefs.getString( SigilCore.LIBRARY_KEYS_PREF ).split( "," ) ) {
+ prefs.setToDefault(key);
+ }
+
+ StringBuffer keys = new StringBuffer();
+
+ for ( ILibrary lib : libraries ) {
+ throw new IllegalStateException( "XXX-FIXME-XXX");
+ }
+
+ prefs.setValue( SigilCore.LIBRARY_KEYS_PREF, keys.toString() );
+
+ return true;
+ }
+
+ private Control initContents(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+ control.setFont(parent.getFont());
+
+ GridLayout grid = new GridLayout(3, false);
+ control.setLayout(grid);
+
+ initRepositories(control);
+
+ return control;
+ }
+
+ private void initRepositories(Composite composite) {
+ // Create controls
+ new Label(composite, SWT.NONE).setText("Libraries:");
+ new Label(composite, SWT.NONE); // Spacer
+ table = new Table(composite, SWT.SINGLE | SWT.BORDER );
+ //table.setFont(control.getFont());
+ btnAdd = new Button(composite, SWT.PUSH);
+ btnAdd.setText("Add...");
+ //add.setFont(control.getFont());
+ btnEdit = new Button(composite, SWT.PUSH);
+ btnEdit.setText("Edit...");
+ //edit.setFont(control.getFont());
+ btnRemove = new Button(composite, SWT.PUSH);
+ btnRemove.setText("Remove");
+ //remove.setFont(control.getFont());
+
+ // Table Model
+ libraries = new TreeSet<ILibrary>(new Comparator<ILibrary> () {
+ public int compare(ILibrary l1, ILibrary l2) {
+ int c = l1.getName().compareTo( l2.getName() );
+ if ( c == 0 ) {
+ c = l1.getVersion().compareTo( l2.getVersion() );
+ }
+ return c;
+ }
+ });
+ libraryView = new TableViewer(table);
+ libraryView.setLabelProvider(new LabelProvider() {
+ public String getText(Object element) {
+ ILibrary rep = (ILibrary) element;
+ return rep.getName() + " " + rep.getVersion();
+ }
+ });
+ libraryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+ libraryView.setInput(libraries);
+
+ // Initialize controls
+ updateButtonStates();
+
+ // Hookup Listeners
+ libraryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateButtonStates();
+ }
+ });
+ btnAdd.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ });
+ btnEdit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ });
+ btnRemove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ });
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+ GridDataFactory buttonGD = GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER);
+ btnAdd.setLayoutData(buttonGD.create());
+ btnEdit.setLayoutData(buttonGD.create());
+ btnRemove.setLayoutData(buttonGD.create());
+ }
+
+ private void updateButtonStates() {
+ ISelection sel = libraryView.getSelection();
+ btnEdit.setEnabled(!sel.isEmpty());
+ btnRemove.setEnabled(!sel.isEmpty());
+ }
+
+
+ private void handleAdd() {
+ LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell());
+ if ( d.open() == Window.OK ) {
+ libraries.add(d.getLibrary());
+ libraryView.refresh();
+ }
+ }
+
+ private void handleEdit() {
+ IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+ boolean change = false;
+
+
+ for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+ ILibrary lib = i.next();
+ LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell(), lib );
+ if ( d.open() == Window.OK ) {
+ libraries.remove(lib);
+ libraries.add(d.getLibrary());
+ change = true;
+ }
+ }
+
+ if ( change ) {
+ libraryView.refresh();
+ }
+ }
+
+ private void handleRemove() {
+ IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+ for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+ libraries.remove(i);
+ }
+ libraryView.refresh();
+ }
+
+ private void loadPreferences() {
+ IPreferenceStore prefs = getPreferenceStore();
+ String keys = prefs.getString( SigilCore.LIBRARY_KEYS_PREF );
+ if ( keys.trim().length() > 0 ) {
+ for ( String key : keys.split( "," ) ) {
+ String libStr = prefs.getString(key);
+ // XXX-FIXME-XXX parse library string
+ // lib = parse(libstr);
+ // libraries.add(lib);
+ }
+ libraryView.refresh();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
new file mode 100644
index 0000000..45ace9f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Shell;
+
+public class OptionalPrompt {
+ public static boolean optionallyPrompt(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+ boolean result = false;
+
+ PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+ switch(value) {
+ case Always:
+ result = true;
+ break;
+ case Never:
+ result = false;
+ break;
+ case Prompt:
+ MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+ result = (dialog.getReturnCode() == IDialogConstants.YES_ID);
+ if(dialog.getToggleState()) {
+ // User said don't ask again... take the current answer as the new preference
+ prefStore.setValue(prefName, result ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+ }
+ }
+
+ return result;
+ }
+
+ public static int optionallyPromptWithCancel(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+ int result = IDialogConstants.NO_ID;
+
+ PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+ switch(value) {
+ case Always:
+ result = IDialogConstants.YES_ID;
+ break;
+ case Never:
+ result = IDialogConstants.NO_ID;
+ break;
+ case Prompt:
+ MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+ result = dialog.getReturnCode();
+ if ( result != IDialogConstants.CANCEL_ID ) {
+ if(dialog.getToggleState()) {
+ // User said don't ask again... take the current answer as the new preference
+ prefStore.setValue(prefName, (result == IDialogConstants.YES_ID) ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
new file mode 100644
index 0000000..07a564c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.jface.preference.PreferencePage;
+
+public abstract class ProjectDependentPreferencesPage extends PreferencePage {
+
+ public ProjectDependentPreferencesPage(String title) {
+ super(title);
+ }
+
+ @Override
+ public boolean performOk() {
+ if ( isDirty() ) {
+ return ProjectUtils.runTaskWithRebuildCheck(new Runnable() {
+ public void run() {
+ doSave();
+ }
+
+ }, getShell());
+ }
+ else {
+ return true;
+ }
+ }
+
+ protected abstract void doSave();
+
+ protected abstract boolean isDirty();
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
new file mode 100644
index 0000000..4a16093
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class SigilPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ @Override
+ protected void createFieldEditors() {
+ RadioGroupFieldEditor impExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Imports for New Exports", 1, new String[][] {
+ new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+ new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+ new String[] { "Never", PromptablePreference.Never.toString() }
+ }, getFieldEditorParent(), true);
+
+ addField(impExpField);
+
+ RadioGroupFieldEditor rebuildExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild Projects On Install Change", 1, new String[][] {
+ new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+ new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+ new String[] { "Never", PromptablePreference.Never.toString() }
+ }, getFieldEditorParent(), true);
+
+ addField(rebuildExpField);
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ public void init(IWorkbench workbench) {
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
new file mode 100644
index 0000000..82cd79c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.osgi.framework.Version;
+
+public class VersionsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ private static final Version SAMPLE_VERSION = new Version(1, 2, 3, "qualifier");
+
+ private IWorkbench workbench;
+
+ private Button btnLowerBoundExact;
+ private Button btnLowerBoundMicro;
+ private Button btnLowerBoundMinor;
+ private Button btnLowerBoundMajor;
+ private Button btnLowerBoundAny;
+
+ private Button btnUpperBoundExact;
+ private Button btnUpperBoundMicro;
+ private Button btnUpperBoundMinor;
+ private Button btnUpperBoundMajor;
+ private Button btnUpperBoundAny;
+
+ private Text txtSampleVersion;
+ private Label lblCalculatedRange;
+ private Text txtMatchVersion;
+ private Label lblMatchResult;
+
+ private VersionRangeBoundingRule lowerBoundRule;
+ private VersionRangeBoundingRule upperBoundRule;
+
+ private Version sampleVersion = SAMPLE_VERSION;
+ private Version matchVersion = null;
+
+ public VersionsPreferencePage() {
+ super();
+ setDescription("Specify the Lower and Upper bounds for a default version range calculated from a point version, e.g. \"1.2.3.qualifier\"");
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ // Create controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Group grpLowerBound = new Group(composite, SWT.NONE);
+ grpLowerBound.setText("Lower Bound");
+ btnLowerBoundExact = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundExact.setText("Exact e.g. [1.2.3.qualifer, ...)");
+ btnLowerBoundMicro = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMicro.setText("Micro e.g. [1.2.3, ...)");
+ btnLowerBoundMinor = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMinor.setText("Minor e.g. [1.2, ...)");
+ btnLowerBoundMajor = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMajor.setText("Major e.g. [1, ...)");
+ btnLowerBoundAny = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundAny.setText("Any e.g. [0, ...)");
+
+ Group grpUpperBound = new Group(composite, SWT.NONE);
+ grpUpperBound.setText("Upper Bound");
+
+ btnUpperBoundExact = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundExact.setText("Exact e.g. [..., 1.2.3.qualifer]");
+ btnUpperBoundMicro = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMicro.setText("Micro e.g. [..., 1.2.4)");
+ btnUpperBoundMinor = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMinor.setText("Minor e.g. [..., 1.3)");
+ btnUpperBoundMajor = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMajor.setText("Major e.g. [..., 2)");
+ btnUpperBoundAny = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundAny.setText("Any e.g. [..., \u221e)");
+
+ Group grpRangeTest = new Group(composite, SWT.NONE);
+ grpRangeTest.setText("Range Test");
+ new Label(grpRangeTest, SWT.NONE).setText("Sample Input Version: ");
+ txtSampleVersion = new Text(grpRangeTest, SWT.BORDER);
+ new Label(grpRangeTest, SWT.NONE).setText("Calculated Version Range: ");
+ lblCalculatedRange = new Label(grpRangeTest, SWT.NONE);
+
+ new Label(grpRangeTest, SWT.NONE).setText("Test: ");
+ txtMatchVersion = new Text(grpRangeTest, SWT.BORDER);
+ new Label(grpRangeTest, SWT.NONE).setText("Result: ");
+ lblMatchResult = new Label(grpRangeTest, SWT.NONE);
+
+ // Initialize controls
+ loadPreferences(false);
+ updateRadioButtons();
+
+ txtSampleVersion.setText(sampleVersion.toString());
+ updateCalculatedRange();
+
+ // Add listeners
+ SelectionListener buttonListener = new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ readRadioButtons();
+ updateCalculatedRange();
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ readRadioButtons();
+ updateCalculatedRange();
+ }
+ };
+ btnLowerBoundAny.addSelectionListener(buttonListener);
+ btnLowerBoundMajor.addSelectionListener(buttonListener);
+ btnLowerBoundMinor.addSelectionListener(buttonListener);
+ btnLowerBoundMicro.addSelectionListener(buttonListener);
+ btnLowerBoundExact.addSelectionListener(buttonListener);
+
+ btnUpperBoundAny.addSelectionListener(buttonListener);
+ btnUpperBoundMajor.addSelectionListener(buttonListener);
+ btnUpperBoundMinor.addSelectionListener(buttonListener);
+ btnUpperBoundMicro.addSelectionListener(buttonListener);
+ btnUpperBoundExact.addSelectionListener(buttonListener);
+
+ txtSampleVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ try {
+ sampleVersion = new Version(txtSampleVersion.getText());
+ } catch (IllegalArgumentException x) {
+ sampleVersion = null;
+ }
+ updateCalculatedRange();
+ }
+ });
+ txtMatchVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ try {
+ matchVersion = new Version(txtMatchVersion.getText());
+ } catch (IllegalArgumentException x) {
+ matchVersion = null;
+ }
+ updateCalculatedRange();
+ }
+ });
+
+ // Layout
+ GridLayout layout = new GridLayout(1, false);
+ layout.verticalSpacing = 20;
+ composite.setLayout(layout);
+
+ grpLowerBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ grpLowerBound.setLayout(new GridLayout(1, false));
+
+ grpUpperBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ grpUpperBound.setLayout(new GridLayout(1, false));
+
+ grpRangeTest.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
+ grpRangeTest.setLayout(new GridLayout(2, false));
+
+ txtSampleVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lblCalculatedRange.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtMatchVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lblMatchResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ return composite;
+ }
+
+ private void loadPreferences(boolean useDefaults) {
+ IPreferenceStore prefs = getPreferenceStore();
+ String lowerBoundStr;
+ if(useDefaults) {
+ lowerBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+ } else {
+ lowerBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+ }
+
+ String upperBoundStr;
+ if(useDefaults) {
+ upperBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+ } else {
+ upperBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+ }
+
+ lowerBoundRule = VersionRangeBoundingRule.valueOf(lowerBoundStr);
+ upperBoundRule = VersionRangeBoundingRule.valueOf(upperBoundStr);
+ }
+
+ private void updateRadioButtons() {
+ switch (lowerBoundRule) {
+ case Exact:
+ btnLowerBoundExact.setSelection(true);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Micro:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(true);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Minor:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(true);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Major:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(true);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Any:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(true);
+ break;
+ }
+
+ switch (upperBoundRule) {
+ case Exact:
+ btnUpperBoundExact.setSelection(true);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Micro:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(true);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Minor:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(true);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Major:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(true);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Any:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(true);
+ }
+ }
+
+ private void readRadioButtons() {
+ if (btnLowerBoundExact.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Exact;
+ } else if (btnLowerBoundMicro.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Micro;
+ } else if (btnLowerBoundMinor.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Minor;
+ } else if (btnLowerBoundMajor.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Major;
+ } else if(btnLowerBoundAny.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Any;
+ }
+
+ if (btnUpperBoundExact.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Exact;
+ } else if (btnUpperBoundMicro.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Micro;
+ } else if (btnUpperBoundMinor.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Minor;
+ } else if (btnUpperBoundMajor.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Major;
+ } else if(btnUpperBoundAny.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Any;
+ }
+ }
+
+ private void updateCalculatedRange() {
+ VersionRange range;
+ String rangeStr;
+ String matchResult;
+
+ if(sampleVersion == null) {
+ range = null;
+ rangeStr = "";
+ } else {
+ range = VersionRange.newInstance(sampleVersion, lowerBoundRule, upperBoundRule);
+ rangeStr = range.toString();
+ }
+ lblCalculatedRange.setText(rangeStr);
+
+ if(matchVersion == null || range == null) {
+ matchResult = "";
+ } else {
+ matchResult = range.contains(matchVersion) ? "MATCH!" : "No Match";
+ }
+ lblMatchResult.setText(matchResult);
+ }
+
+ public void init(IWorkbench workbench) {
+ this.workbench = workbench;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ public boolean performOk() {
+ getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_LOWER_BOUND, lowerBoundRule.name());
+ getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_UPPER_BOUND, upperBoundRule.name());
+
+ return true;
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ loadPreferences(true);
+ updateRadioButtons();
+ updateCalculatedRange();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
new file mode 100644
index 0000000..9e205d3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.installs;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstallType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class OSGiInstallsPreferencePage extends ProjectDependentPreferencesPage implements
+ IWorkbenchPreferencePage {
+
+ private class Install {
+ private String id;
+ private String location;
+ private IOSGiInstallType type;
+
+ private Install(String id, String location) {
+ this.id = id;
+ this.location = location;
+ }
+
+ private IOSGiInstallType getType() {
+ if ( type == null ) {
+ type = SigilCore.getInstallManager().findInstallType(location);
+ }
+ return type;
+ }
+ }
+
+ private HashMap<String, Install> installs = new HashMap<String, Install>();
+ private CheckboxTableViewer viewer;
+ private boolean changed;
+
+ public OSGiInstallsPreferencePage() {
+ super("OSGi Installs");
+ }
+
+ public void init(IWorkbench workbench) {
+ }
+
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ buildComponents(control);
+
+ load();
+
+ checkValid();
+
+ return control;
+ }
+
+ @Override
+ protected boolean isDirty() {
+ return changed;
+ }
+
+
+ private void buildComponents(Composite control) {
+ new Label(control, SWT.NONE).setText("Installs:");
+ new Label(control, SWT.NONE); // padding
+
+ Table table = new Table(control, SWT.CHECK | SWT.SINGLE | SWT.BORDER);
+
+ Button add = new Button(control, SWT.PUSH);
+ add.setText("Add");
+ add.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ add();
+ }
+ });
+
+ final Button remove = new Button(control, SWT.PUSH);
+ remove.setEnabled(false);
+ remove.setText("Remove");
+ remove.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ remove();
+ }
+ });
+
+ // viewers
+ viewer = new CheckboxTableViewer(table);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setLabelProvider( new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Install i = (Install) element;
+ IOSGiInstallType type = i.getType();
+ if ( type == null ) {
+ return "<invalid> [" + i.location + "]";
+ }
+ else {
+ return type.getName() + " " + type.getVersion() + " [" + i.location + "]";
+ }
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Install i = (Install) element;
+ IOSGiInstallType type = i.getType();
+
+ if (type == null) {
+ return null;
+ } else {
+ return type.getIcon();
+ }
+ }
+ });
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean enabled = !event.getSelection().isEmpty();
+ remove.setEnabled(enabled );
+ }
+ });
+
+ viewer.addCheckStateListener( new ICheckStateListener () {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ if ( event.getChecked() ) {
+ changed = true;
+ }
+ viewer.setCheckedElements( new Object[] { event.getElement() } );
+ }
+ });
+
+ viewer.setInput(installs.values());
+
+ // layout
+ control.setLayout( new GridLayout(2, false) );
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ private void load() {
+ String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+ if ( pref != null && pref.length() > 0 ) {
+ for ( String id : pref.split(",") ) {
+ String loc = getPreferenceStore().getString( SigilCore.OSGI_INSTALL_PREFIX + id );
+ installs.put( id, new Install( id, loc ) );
+ }
+ }
+
+ viewer.refresh();
+
+ if ( !installs.isEmpty() ) {
+ String defId = getPreferenceStore().getString( SigilCore.OSGI_DEFAULT_INSTALL_ID );
+ if ( defId == null || defId.trim().length() == 0 ) {
+ viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+ }
+ else {
+ viewer.setCheckedElements( new Object[] { installs.get( defId ) } );
+ }
+ }
+ }
+
+ protected void doSave() {
+ // zero out old configs
+ String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+ if ( pref != null && pref.length() > 0 ) {
+ for ( String id : pref.split(",") ) {
+ getPreferenceStore().setToDefault( SigilCore.OSGI_INSTALL_PREFIX + id );
+ }
+ }
+
+ // store new configs
+ if ( installs.isEmpty() ) {
+ getPreferenceStore().setToDefault(SigilCore.OSGI_INSTALLS);
+ getPreferenceStore().setToDefault(SigilCore.OSGI_DEFAULT_INSTALL_ID);
+ }
+ else {
+ StringBuffer buf = new StringBuffer();
+ for (Install i : installs.values() ) {
+ if ( buf.length() > 0 ) {
+ buf.append(",");
+ }
+ buf.append( i.id );
+ getPreferenceStore().setValue( SigilCore.OSGI_INSTALL_PREFIX + i.id, i.location );
+ }
+
+ getPreferenceStore().setValue( SigilCore.OSGI_INSTALLS, buf.toString() );
+ Install def = (Install) viewer.getCheckedElements()[0];
+ getPreferenceStore().setValue(SigilCore.OSGI_DEFAULT_INSTALL_ID, def.id);
+ }
+ changed = false;
+ }
+
+ private boolean isOK() {
+ return installs.isEmpty() || viewer.getCheckedElements().length > 0;
+ }
+
+ private void add() {
+ Shell shell = SigilUI.getActiveWorkbenchShell();
+ DirectoryDialog dialog = new DirectoryDialog(shell);
+ String dir = dialog.open();
+ if ( dir != null ) {
+ Install install = new Install( UUID.randomUUID().toString(), dir );
+ if ( install.getType() == null ) {
+ MessageDialog.openError(shell, "Error", "Invalid OSGi install directory" );
+ }
+ else {
+ boolean empty = installs.isEmpty();
+
+ installs.put( install.id, install );
+ viewer.refresh();
+
+ if ( empty ) {
+ viewer.setCheckedElements( new Object[] { install } );
+ }
+
+ checkValid();
+ changed = true;
+ }
+ }
+ }
+
+ private void checkValid() {
+ if ( isOK() ) {
+ setErrorMessage(null);
+ setValid(true);
+ }
+ else {
+ setErrorMessage("Missing default OSGi install");
+ setValid(false);
+ }
+ }
+
+ private void remove() {
+ IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
+ Install i = (Install) sel.getFirstElement();
+ boolean def = viewer.getChecked(i);
+ installs.remove(i.id);
+ viewer.refresh();
+ if ( def && installs.size() > 0 ) {
+ viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+ }
+ checkValid();
+ changed = true;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
new file mode 100644
index 0000000..cbfa165
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.project;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.osgi.service.prefs.BackingStoreException;
+
+public class ProjectPropertyPage extends PropertyPage implements
+ IWorkbenchPropertyPage {
+
+ private boolean projectSpecific;
+ private ComboViewer setView;
+ private Composite settings;
+ private Button projectSpecificBtn;
+
+ @Override
+ protected Control createContents(Composite parent) {
+ final Composite control = new Composite(parent, SWT.NONE);
+
+ projectSpecificBtn = new Button(control, SWT.CHECK);
+ projectSpecificBtn.setText( "Enable project specific settings");
+ projectSpecificBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setProjectSpecific(!projectSpecific);
+ }
+ });
+
+ Label link = new Label( control, SWT.UNDERLINE_SINGLE );
+ link.addMouseListener( new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, SigilCore.REPOSITORIES_PREFERENCES_ID, null, null);
+ dialog.open();
+ }
+ });
+
+ link.setText("Configure workspace settings" );
+
+ settings = new Composite( control, SWT.BORDER );
+ settings.setLayout( new GridLayout(1,false));
+ createSettings( settings );
+
+ setFonts(control);
+
+ // layout
+ control.setLayout( new GridLayout(2, false));
+ projectSpecificBtn.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false ) );
+ settings.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1 ) );
+
+ // load settings
+ String currentSet = getCurrentSet();
+
+ if ( currentSet == null ) {
+ setProjectSpecific(false);
+ }
+ else {
+ setView.setSelection( new StructuredSelection(currentSet) );
+ setProjectSpecific(true);
+ }
+
+ return control;
+ }
+
+ private void setFonts(Composite control) {
+ Composite p = control.getParent();
+ for ( Control c : control.getChildren() ) {
+ c.setFont( p.getFont() );
+ if ( c instanceof Composite ) {
+ setFonts( (Composite) c );
+ }
+ }
+ }
+
+ private void setProjectSpecific(boolean projectSpecific) {
+ if ( this.projectSpecific != projectSpecific ) {
+ this.projectSpecific = projectSpecific;
+ settings.setEnabled(projectSpecific);
+ for ( Control c : settings.getChildren() ) {
+ c.setEnabled( projectSpecific );
+ }
+ projectSpecificBtn.setSelection(projectSpecific);
+ }
+ }
+
+ private void createSettings(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ new Label( control, SWT.NONE).setText( "Repository Set:" );
+ Combo combo = new Combo(control, SWT.SINGLE);
+
+ setView = new ComboViewer(combo);
+ setView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ setView.setInput( SigilCore.getRepositoryConfiguration().loadRepositorySets().keySet() );
+
+ // layout
+ control.setLayout( new GridLayout(2, false ) );
+ }
+
+ private String getCurrentSet() {
+ try {
+ IProject p = (IProject) getElement().getAdapter(IProject.class);
+ ISigilProjectModel model = SigilCore.create(p);
+ return model.getPreferences().get( SigilCore.REPOSITORY_SET, null );
+ } catch (CoreException e) {
+ SigilCore.error("Failed to read repository set", e );
+ return null;
+ }
+ }
+
+ @Override
+ public boolean okToLeave() {
+ if ( projectSpecific ) {
+ if ( setView.getSelection().isEmpty() ) {
+ setErrorMessage("Must select a repository set");
+ return false;
+ }
+ }
+ setErrorMessage(null);
+ return true;
+ }
+
+ @Override
+ public boolean performOk() {
+ return ProjectUtils.runTaskWithRebuildCheck( new Callable<Boolean>() {
+ public Boolean call() throws CoreException, BackingStoreException {
+ String set = null;
+ if ( projectSpecific ) {
+ set = (String) ((IStructuredSelection) setView.getSelection()).getFirstElement();
+ }
+
+ IProject p = (IProject) getElement().getAdapter(IProject.class);
+ ISigilProjectModel model = SigilCore.create(p);
+ model.getPreferences().put( SigilCore.REPOSITORY_SET, set );
+ model.getPreferences().flush();
+ return true;
+ }
+
+ }, getShell());
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
new file mode 100644
index 0000000..bf410ae
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.Wizard;
+
+public class NewRepositoryWizard extends Wizard {
+
+ private IRepositoryModel repository;
+
+ private RepositoryTypeSelectionPage page = new RepositoryTypeSelectionPage();
+
+ public void addPages() {
+ addPage( page );
+ }
+
+ @Override
+ public boolean performFinish() {
+ repository = page.getRepository();
+ return true;
+ }
+ @Override
+ public boolean needsPreviousAndNextButtons() {
+ return true;
+ }
+
+ public IRepositoryModel getRepository() {
+ return repository;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
new file mode 100644
index 0000000..9503a98
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class RepositoriesPreferencePage extends ProjectDependentPreferencesPage implements
+ IWorkbenchPreferencePage {
+
+ private boolean changed;
+ private RepositoriesView viewPage;
+ private RepositorySetsView setPage;
+
+ public RepositoriesPreferencePage() {
+ super( "Repository Preferences" );
+ }
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = initContents( parent );
+ return control;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ protected void changed() {
+ changed = true;
+ updateApplyButton();
+ }
+
+ private Control initContents(Composite parent) {
+ viewPage = new RepositoriesView(this);
+ setPage = new RepositorySetsView(this);
+
+ Composite control = new Composite(parent, SWT.NONE);
+
+ TabFolder folder = new TabFolder(control, SWT.TOP);
+
+ TabItem view = new TabItem(folder, SWT.NONE);
+ view.setText("Repositories");
+ view.setControl(viewPage.createContents(folder) );
+
+ TabItem sets = new TabItem(folder, SWT.NONE);
+ sets.setText("Sets");
+ sets.setControl(setPage.createContents(folder) );
+
+ control.setLayout(new GridLayout(1, true));
+ folder.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ return control;
+ }
+
+ public void init(IWorkbench workbench) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void doSave() {
+ try {
+ IRepositoryConfiguration config = SigilCore.getRepositoryConfiguration();
+ config.saveRepositories(viewPage.getRepositories());
+ config.saveRepositorySets(setPage.getSets());
+ IRepositorySet defaultSet = new RepositorySet(setPage.getDefaultRepositories());
+ config.setDefaultRepositorySet(defaultSet);
+
+ setErrorMessage(null);
+ getApplyButton().setEnabled(false);
+ changed = false;
+ } catch (CoreException e) {
+ setErrorMessage("Failed to save repositories:" + e.getStatus().getMessage());
+ SigilCore.error("Failed to save repositories", e);
+ }
+ }
+
+ @Override
+ protected boolean isDirty() {
+ return changed;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
new file mode 100644
index 0000000..f9394e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.actions.RefreshRepositoryAction;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoriesView {
+ private final RepositoriesPreferencePage page;
+
+ private List<IRepositoryModel> repositories;
+
+ private TableViewer repositoryView;
+
+ public RepositoriesView(RepositoriesPreferencePage page) {
+ this.page = page;
+ }
+
+ public Control createContents(Composite parent) {
+ // Create Controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Table table = new Table(composite, SWT.MULTI | SWT.BORDER);
+
+ // Table Viewer Setup
+ repositoryView = new TableViewer(table);
+ repositoryView.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IRepositoryModel rep = (IRepositoryModel) element;
+ return rep.getName();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ IRepositoryModel rep = (IRepositoryModel) element;
+ return rep.getType().getIcon();
+ }
+ });
+
+ repositoryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+
+ createButtons(composite, repositoryView);
+
+ repositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+ repositoryView.setInput(repositories);
+
+ return composite;
+ }
+
+ private void createButtons(final Composite composite, final TableViewer repositoryView) {
+ final Button add = new Button(composite, SWT.PUSH);
+ add.setText("Add...");
+ add.setEnabled(true);
+
+ final Button edit = new Button(composite, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ final Button refresh = new Button(composite, SWT.PUSH);
+ refresh.setText("Refresh");
+ refresh.setEnabled( false );
+
+ // Listeners
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ add(composite);
+ }
+ });
+
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ edit(composite, sel);
+ }
+ });
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ remove(sel);
+ }
+ });
+
+ refresh.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ refresh(composite, sel);
+ }
+ });
+
+ repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean selected = !event.getSelection().isEmpty();
+ if ( selected ) {
+ refresh.setEnabled(true);
+
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+
+ checkEditEnabled(edit, sel);
+ checkRemoveEnabled(remove, sel);
+ }
+ else {
+ refresh.setEnabled(false);
+ edit.setEnabled(false);
+ remove.setEnabled(false);
+ }
+ }
+ });
+
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void checkRemoveEnabled(Button button, IStructuredSelection sel) {
+ boolean alldynamic = true;
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ IRepositoryModel model = (IRepositoryModel) i.next();
+ if ( !model.getType().isDynamic() ) {
+ alldynamic = false;
+ break;
+ }
+ }
+ button.setEnabled(alldynamic);
+ }
+
+ private void checkEditEnabled(Button edit, IStructuredSelection sel) {
+ if ( sel.size() == 1 ) {
+ IRepositoryModel element = (IRepositoryModel) sel.getFirstElement();
+ if ( WizardHelper.hasWizard( element.getType()) ) {
+ edit.setEnabled(true);
+ }
+ else {
+ edit.setEnabled(false);
+ }
+ }
+ else {
+ edit.setEnabled(false);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void refresh(Control parent, IStructuredSelection sel) {
+ ArrayList<IRepositoryModel> models = new ArrayList<IRepositoryModel>(sel.size());
+
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ IRepositoryModel model = (IRepositoryModel) i.next();
+ models.add( model );
+ }
+
+ new RefreshRepositoryAction(models.toArray(new IRepositoryModel[models.size()])).run();
+ }
+
+ private void add(Control parent) {
+ NewRepositoryWizard wizard = new NewRepositoryWizard();
+ WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+ if ( dialog.open() == Window.OK ) {
+ repositories.add(wizard.getRepository());
+ updated();
+ }
+ }
+
+ private void edit(Control parent, IStructuredSelection sel) {
+ IRepositoryModel model = (IRepositoryModel) sel.getFirstElement();
+ try {
+ RepositoryWizard wizard = WizardHelper.loadWizard(model.getType());
+ wizard.init(model);
+ WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+ if ( dialog.open() == Window.OK ) {
+ updated();
+ }
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to load wizard", e);
+ MessageDialog.openError(getShell(parent), "Error", "Failed to load wizard:" + e.getStatus().getMessage() );
+ }
+ }
+
+ private Shell getShell(Control parent) {
+ return parent.getShell();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void remove(IStructuredSelection sel) {
+ boolean change = false;
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ change = repositories.remove(i.next());
+ }
+
+ if ( change ) {
+ updated();
+ }
+ }
+
+ private void updated() {
+ repositoryView.refresh();
+ page.changed();
+ }
+
+ public List<IRepositoryModel> getRepositories() {
+ return repositories;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
new file mode 100644
index 0000000..8136f55
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class RepositorySetDialog extends TitleAreaDialog {
+
+ private CheckboxTableViewer viewer;
+ private Text nameTxt;
+ private Button upBtn;
+ private Button downBtn;
+ private final String setName;
+ private List<IRepositoryModel> repositories;
+ private final boolean nameEditable;
+ private final Set<String> set;
+
+ private String newName;
+
+ public RepositorySetDialog(Shell shell, Set<String> set) {
+ this(shell, null, true, set);
+ }
+
+ public RepositorySetDialog(Shell parent, RepositoryViewData data, boolean nameEditable, Set<String> set) {
+ super(parent);
+ this.set = set;
+ this.setName = data == null ? "" : data.getName();
+ this.repositories = data == null ? new ArrayList<IRepositoryModel>() : new ArrayList<IRepositoryModel>(Arrays.asList(data.getRepositories()));
+ this.nameEditable = nameEditable;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite area = (Composite) super.createDialogArea(parent);
+ createControl(area);
+ return area;
+ }
+
+ public void createControl(Composite parent) {
+ // controls
+ Composite body = new Composite(parent, SWT.NONE);
+ body.setLayoutData( new GridData(GridData.FILL_BOTH) );
+
+ if ( nameEditable ) {
+ new Label( body, SWT.NONE ).setText( "Name" );
+
+ nameTxt = new Text( body, SWT.BORDER );
+
+ nameTxt.setText(setName);
+
+ nameTxt.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ checkComplete();
+ }
+ });
+ }
+
+ Composite table = new Composite(body, SWT.NONE);
+ table.setLayout( new GridLayout(2, false ) );
+ createTable( table );
+
+ // layout
+ body.setLayout( new GridLayout( 2, false ) );
+ if ( nameEditable ) {
+ nameTxt.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false) );
+ }
+ table.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1) );
+ }
+
+ public RepositoryViewData getData() {
+ String name = nameEditable ? newName : setName;
+ IRepositoryModel[] reps = repositories.toArray( new IRepositoryModel[repositories.size()]);
+ return new RepositoryViewData( name, reps );
+ }
+
+ private void checkComplete() {
+ if ( nameEditable ) {
+ String name = nameTxt.getText();
+ if ( !name.equals( setName ) && set.contains( name ) ) {
+ setErrorMessage("Set " + name + " already exists" );
+ Button b = getButton(IDialogConstants.OK_ID);
+ b.setEnabled(false);
+ }
+ }
+ setErrorMessage(null);
+ Button b = getButton(IDialogConstants.OK_ID);
+ b.setEnabled(true);
+ }
+
+
+ @Override
+ protected void okPressed() {
+ if ( nameEditable ) {
+ newName = nameTxt.getText();
+ }
+ repositories = getRepositories();
+ super.okPressed();
+ }
+
+ private void createTable(Composite body) {
+ createViewer(body);
+
+ Composite btns = new Composite(body, SWT.NONE);
+ btns.setLayout( new GridLayout( 1, true ) );
+
+ createButtons(btns);
+
+ // layout
+ viewer.getTable().setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) );
+ btns.setLayoutData( new GridData( SWT.RIGHT, SWT.TOP, false, false ) );
+ }
+
+ private void createButtons(Composite parent) {
+ upBtn = new Button(parent, SWT.PUSH);
+ upBtn.setText( "Up" );
+ upBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ up();
+ }
+ });
+
+ downBtn = new Button(parent, SWT.PUSH);
+ downBtn.setText( "Down" );
+ downBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ down();
+ }
+ });
+
+ setUpDownEnabled(false);
+ }
+
+ private void up() {
+ IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+ int i = repositories.indexOf(model);
+ if ( i > 0 ) {
+ repositories.remove( i );
+ repositories.add( i - 1, model );
+ viewer.refresh();
+ }
+ }
+
+ private void down() {
+ IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+ int i = repositories.indexOf(model);
+ if ( i < repositories.size() - 1 ) {
+ repositories.remove( i );
+ repositories.add( i + 1, model );
+ viewer.refresh();
+ }
+ }
+
+ private void createViewer(Composite parent) {
+ viewer = CheckboxTableViewer.newCheckList(parent, SWT.BORDER);
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setUpDownEnabled( !viewer.getSelection().isEmpty() );
+ }
+ });
+
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setLabelProvider( new DefaultLabelProvider() {
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ public String getText(Object element) {
+ IRepositoryModel m = (IRepositoryModel) element;
+ return m.getName();
+ }
+ });
+
+ viewer.setInput( repositories );
+
+ for ( IRepositoryModel m : repositories ) {
+ viewer.setChecked(m, true);
+ }
+
+ List<IRepositoryModel> allRepositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+
+ for ( IRepositoryModel m : allRepositories ) {
+ if ( !repositories.contains(m) ) {
+ repositories.add(m);
+ }
+ }
+
+ viewer.refresh();
+ }
+
+ private void setUpDownEnabled(boolean enabled) {
+ upBtn.setEnabled(enabled);
+ downBtn.setEnabled(enabled);
+ }
+
+ private List<IRepositoryModel> getRepositories() {
+ ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+
+ for ( IRepositoryModel m : repositories ) {
+ if ( viewer.getChecked(m) ) {
+ reps.add( m );
+ }
+ }
+
+ return reps;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
new file mode 100644
index 0000000..0470675
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositorySetsView {
+ private static final String DEFAULT = "default";
+
+ private final RepositoriesPreferencePage page;
+
+ private ArrayList<RepositoryViewData> sets = new ArrayList<RepositoryViewData>();
+
+ private TableViewer setView;
+
+ private RepositoryViewData defaultSet;
+
+ public RepositorySetsView(RepositoriesPreferencePage page) {
+ this.page = page;
+ }
+
+ public Control createContents(Composite parent) {
+ // Create Controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Table table = new Table(composite, SWT.SINGLE | SWT.BORDER);
+
+ // Table Viewer Setup
+ setView = new TableViewer(table);
+
+ setView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ defaultSet = new RepositoryViewData(DEFAULT, SigilCore.getRepositoryConfiguration().getDefaultRepositorySet().getRepositories());
+
+ sets.add( defaultSet );
+
+ for( Map.Entry<String, IRepositorySet> e : SigilCore.getRepositoryConfiguration().loadRepositorySets().entrySet() ) {
+ IRepositorySet s = e.getValue();
+ sets.add( new RepositoryViewData( e.getKey(), s.getRepositories() ) );
+ }
+
+ setView.setLabelProvider( new DefaultLabelProvider() {
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ public String getText(Object element) {
+ RepositoryViewData data = (RepositoryViewData) element;
+ return data.getName();
+ }
+ });
+
+ setView.setInput(sets);
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+
+ createButtons(composite);
+
+ return composite;
+ }
+
+ private void createButtons(final Composite composite) {
+ final Button add = new Button(composite, SWT.PUSH);
+ add.setText("Add...");
+ add.setEnabled(true);
+
+ final Button edit = new Button(composite, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+ // Listeners
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ add(composite);
+ }
+ });
+
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+ edit(composite, sel);
+ }
+ });
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+ remove(sel);
+ }
+ });
+
+ setView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean enabled = !event.getSelection().isEmpty();
+ if ( enabled ) {
+ RepositoryViewData element = (RepositoryViewData) ((IStructuredSelection) event.getSelection()).getFirstElement();
+ edit.setEnabled(true);
+ remove.setEnabled( element != defaultSet );
+ }
+ else {
+ edit.setEnabled(false);
+ remove.setEnabled(false);
+ }
+ }
+ });
+
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ private void add(Control parent) {
+ RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), getNames());
+ if ( wizard.open() == Window.OK ) {
+ sets.add( wizard.getData() );
+ updated();
+ }
+ }
+
+ private void edit(Control parent, IStructuredSelection sel) {
+ RepositoryViewData data = (RepositoryViewData) sel.getFirstElement();
+ RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), data, data != defaultSet, getNames());
+ if ( wizard.open() == Window.OK ) {
+ if ( data != defaultSet ) {
+ data.setName( wizard.getData().getName() );
+ }
+ data.setRepositories( wizard.getData().getRepositories() );
+ updated();
+ }
+ }
+
+ private Set<String> getNames() {
+ HashSet<String> names = new HashSet<String>();
+
+ for ( RepositoryViewData view : sets ) {
+ if ( view != defaultSet ) {
+ names.add( view.getName() );
+ }
+ }
+
+ return names;
+ }
+
+ private Shell getShell(Control parent) {
+ return parent.getShell();
+ }
+
+ private void remove(IStructuredSelection sel) {
+ if ( sets.remove(sel.getFirstElement())) {
+ updated();
+ }
+ }
+
+ private void updated() {
+ setView.refresh();
+ page.changed();
+ }
+
+ public Map<String, IRepositorySet> getSets() {
+ HashMap<String, IRepositorySet> ret = new HashMap<String, IRepositorySet>();
+
+ for ( RepositoryViewData data : sets ) {
+ if ( data != defaultSet ) {
+ IRepositorySet set = new RepositorySet(data.getRepositories());
+ ret.put( data.getName(), set );
+ }
+ }
+
+ return ret;
+ }
+
+ public IRepositoryModel[] getDefaultRepositories() {
+ return defaultSet.getRepositories();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
new file mode 100644
index 0000000..3dfe8b8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardSelectionPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoryTypeSelectionPage extends WizardSelectionPage implements IWizardPage {
+
+ private static final String TITLE = "Select Repository";
+
+ private TableViewer repositoryView;
+ private IRepositoryModel repositoryElement;
+
+ public RepositoryTypeSelectionPage() {
+ super(TITLE);
+ setTitle(TITLE);
+ }
+
+ public void createControl(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ // components
+ new Label(control, SWT.NONE).setText("Repositories" );
+ Table table = new Table(control, SWT.SINGLE | SWT.BORDER);
+
+ // layout
+ control.setLayout( new GridLayout(1, true) );
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // view
+ repositoryView = new TableViewer(table);
+ repositoryView.setLabelProvider( new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IRepositoryType rep = (IRepositoryType) element;
+ return rep.getType();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ IRepositoryType rep = (IRepositoryType) element;
+ return rep.getIcon();
+ }
+ });
+
+ repositoryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ if ( !event.getSelection().isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ IRepositoryType type = (IRepositoryType) sel.getFirstElement();
+ repositoryElement = SigilCore.getRepositoryConfiguration().newRepositoryElement(type);
+ selectWizardNode(new RepositoryWizardNode(repositoryElement));
+ }
+ }
+ });
+
+ ArrayList<IRepositoryType> descriptors = new ArrayList<IRepositoryType>(SigilCore.getRepositoryConfiguration().loadRepositoryTypes());
+
+ for ( Iterator<IRepositoryType> i = descriptors.iterator(); i.hasNext(); ) {
+ if ( !i.next().isDynamic() ) {
+ i.remove();
+ }
+ }
+
+ repositoryView.setInput( descriptors );
+
+ setControl(control);
+ }
+
+ public void selectWizardNode(RepositoryWizardNode node) {
+ setSelectedNode(node);
+ }
+
+ public RepositoryWizardNode getSelectedWizardNode() {
+ return (RepositoryWizardNode) getSelectedNode();
+ }
+
+ public IRepositoryModel getRepository() {
+ return repositoryElement;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
new file mode 100644
index 0000000..85124c7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+
+class RepositoryViewData {
+ private String name;
+ private IRepositoryModel[] reps;
+
+ RepositoryViewData(String name, IRepositoryModel[] reps) {
+ this.name = name;
+ this.reps = reps;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ IRepositoryModel[] getRepositories() {
+ return reps;
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ void setRepositories(IRepositoryModel[] reps) {
+ this.reps = reps;
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
new file mode 100644
index 0000000..5ea458a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardNode;
+import org.eclipse.swt.graphics.Point;
+
+public class RepositoryWizardNode implements IWizardNode {
+
+ private IRepositoryModel repository;
+
+ private RepositoryWizard wizard;
+
+ public RepositoryWizardNode(IRepositoryModel repository) {
+ this.repository = repository;
+ }
+
+ public void dispose() {
+ if ( wizard != null ) {
+ wizard.dispose();
+ wizard = null;
+ }
+ }
+
+ public Point getExtent() {
+ return new Point(-1, -1);
+ }
+
+ public IWizard getWizard() {
+ if ( wizard == null ) {
+ try {
+ wizard = WizardHelper.loadWizard(repository.getType());
+ wizard.init( repository );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to create wizard for " + repository.getType(), e);
+ }
+ }
+ return wizard;
+ }
+
+ public IRepositoryModel getRepository() {
+ return repository;
+ }
+
+ public boolean isContentCreated() {
+ return wizard != null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
new file mode 100644
index 0000000..101b57e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+public class WizardHelper {
+ public static RepositoryWizard loadWizard(IRepositoryType type) throws CoreException {
+ IConfigurationElement e = findWizardConfig(type.getId());
+
+ if ( e == null ) {
+ throw SigilCore.newCoreException("No wizard registered for repository " + type, null);
+ }
+
+ return (RepositoryWizard) e.createExecutableExtension("class");
+ }
+
+ public static boolean hasWizard(IRepositoryType type) {
+ return findWizardConfig(type.getId()) != null;
+ }
+
+ private static IConfigurationElement findWizardConfig(String id) {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint p = registry.getExtensionPoint(SigilUI.REPOSITORY_WIZARD_EXTENSION_POINT_ID);
+
+ for ( IExtension e : p.getExtensions() ) {
+ for ( IConfigurationElement c : e.getConfigurationElements() ) {
+ if ( id.equals( c.getAttribute("repository") ) ) {
+ return c;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
new file mode 100644
index 0000000..163154d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportPackageProposal implements IJavaCompletionProposal {
+
+ private IPackageExport e;
+ private ISigilProjectModel n;
+
+ public ImportPackageProposal(IPackageExport e, ISigilProjectModel n) {
+ this.e = e;
+ this.n = n;
+ }
+
+ public int getRelevance() {
+ return 100;
+ }
+
+ public void apply(IDocument document) {
+ try {
+
+ final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ i.setPackageName(e.getPackageName());
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = e.getVersion();
+ VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ i.setVersions( selectedVersions );
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ n.getBundle().getBundleInfo().addImport( i );
+ n.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ } catch (ModelElementFactoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public IContextInformation getContextInformation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getDisplayString() {
+ return "Import package " + e.getPackageName() + " version " + e.getVersion() + " to bundle";
+ }
+
+ public Image getImage() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
new file mode 100644
index 0000000..ac3f9ba
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.search.SigilSearch;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.ui.text.java.IInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;
+
+@SuppressWarnings("restriction")
+public class ImportQuickFixProcessor implements IQuickFixProcessor {
+
+ private static final Object SYNC_FLAG = new Object();
+
+ public boolean hasCorrections(ICompilationUnit unit, int problemId) {
+ switch ( problemId ) {
+ case IProblem.ImportNotFound:
+ case IProblem.ForbiddenReference:
+ case IProblem.NotVisibleType:
+ case IProblem.UndefinedType:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
+ IProblemLocation[] locations) throws CoreException {
+ try {
+ HashMap<Object, IJavaCompletionProposal> results = new HashMap<Object, IJavaCompletionProposal>();
+
+ ISigilProjectModel project = findProject(context);
+
+ if ( project != null ) {
+ for ( int i = 0; i < locations.length; i++ ) {
+ switch ( locations[i].getProblemId() ) {
+ case IProblem.ForbiddenReference:
+ handleImportNotFound(project, context, locations[i], results);
+ break;
+ case IProblem.ImportNotFound:
+ handleImportNotFound(project, context, locations[i], results);
+ break;
+ case IProblem.IsClassPathCorrect:
+ handleIsClassPathCorrect(project, context, locations[i], results);
+ break;
+ case IProblem.UndefinedType:
+ handleUndefinedType(project, context, locations[i], results);
+ break;
+ case IProblem.UndefinedName:
+ handleUndefinedName(project, context, locations[i], results);
+ break;
+ }
+ }
+ }
+
+ return (IJavaCompletionProposal[])results.values().toArray(new IJavaCompletionProposal[results.size()]);
+ }
+ catch ( RuntimeException e ) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ private void handleUndefinedName(ISigilProjectModel project,
+ IInvocationContext context, IProblemLocation problem,
+ HashMap<Object, IJavaCompletionProposal> results) {
+ Name node = findNode(context, problem);
+
+ if ( node == null ) {
+ return;
+ }
+ addSearchResults(node, project, results);
+ }
+
+ private void handleIsClassPathCorrect(ISigilProjectModel project, final IInvocationContext context,
+ IProblemLocation problemLocation,
+ final HashMap<Object, IJavaCompletionProposal> results) {
+ for ( final String type : problemLocation.getProblemArguments() ) {
+ final String iPackage = type.substring(0, type.lastIndexOf("."));
+
+ for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) {
+ results.put( type, new ImportPackageProposal( pe, project ) );
+ }
+ }
+
+ if ( !results.containsKey(SYNC_FLAG) ) {
+ //results.put( SYNC_FLAG, null);
+ }
+ }
+
+ private void handleUndefinedType(ISigilProjectModel project, IInvocationContext context,
+ IProblemLocation problem,
+ HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+ Name node = findNode(context, problem);
+
+ if ( node == null ) {
+ return;
+ }
+ addSearchResults(node, project, results);
+ }
+
+ private void handleImportNotFound(ISigilProjectModel project, final IInvocationContext context, IProblemLocation location, final HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+ ASTNode selectedNode= location.getCoveringNode(context.getASTRoot());
+ if (selectedNode == null) return;
+
+ if ( selectedNode instanceof ClassInstanceCreation ) {
+ ClassInstanceCreation c = (ClassInstanceCreation) selectedNode;
+ Type t = c.getType();
+ Name node = findName( t );
+ if ( node != null ) {
+ addSearchResults(node, project, results);
+ }
+ }
+ else {
+ for ( final String iPackage : readPackage(selectedNode, location) ) {
+ if ( !results.containsKey(iPackage) ) {
+ for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) {
+ results.put( iPackage, new ImportPackageProposal( pe, project ) );
+ }
+ }
+ }
+ }
+ }
+
+ private void addSearchResults(Name node, ISigilProjectModel project,
+ HashMap<Object, IJavaCompletionProposal> results) {
+ for ( ISearchResult result : SigilSearch.findProviders( node.getFullyQualifiedName(), project, null) ) {
+ if ( project.getBundle().findImport( result.getPackageName() ) == null ) {
+ String type = result.getPackageName() + "." + node.getFullyQualifiedName();
+ results.put( type, new ImportSearchResultProposal( SigilUI.getActiveWorkbenchShell(), result, node, project ) );
+ }
+ }
+ }
+
+ private Name findName(Type t) {
+ if ( t.isSimpleType() ) {
+ SimpleType st = (SimpleType) t;
+ return st.getName();
+ }
+ else if ( t.isArrayType() ) {
+ ArrayType at = (ArrayType) t;
+ return findName(at.getElementType());
+ }
+ else {
+ return null;
+ }
+ }
+
+ private Name findNode(IInvocationContext context, IProblemLocation problem) {
+ ASTNode selectedNode= problem.getCoveringNode(context.getASTRoot());
+ if (selectedNode == null) {
+ return null;
+ }
+
+ while (selectedNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
+ selectedNode= selectedNode.getParent();
+ }
+
+ Name node= null;
+
+ if (selectedNode instanceof Type) {
+ node = findName( (Type) selectedNode );
+ } else if (selectedNode instanceof Name) {
+ node= (Name) selectedNode;
+ }
+
+ return node;
+ }
+
+ private ISigilProjectModel findProject(IInvocationContext context) throws CoreException {
+ IProject project = context.getCompilationUnit().getJavaProject().getProject();
+ if ( project.hasNature( SigilCore.NATURE_ID ) ) {
+ return SigilCore.create(project);
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String[] readPackage(ASTNode selectedNode, IProblemLocation location) {
+ ArrayList<String> packages = new ArrayList<String>();
+
+ ImportDeclaration id = (ImportDeclaration) ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
+
+ if ( id == null ) {
+ MethodInvocation m = (MethodInvocation) ASTNodes.getParent(selectedNode, ASTNode.METHOD_INVOCATION);
+
+ if (m != null) {
+ packages.add( readPackage( m ) );
+ while ( m.getExpression() != null && m.getExpression() instanceof MethodInvocation) {
+ m = (MethodInvocation) m.getExpression();
+ packages.add( readPackage( m ) );
+ }
+ }
+ }
+ else {
+ if ( id.isOnDemand() ) {
+ packages.add(id.getName().toString());
+ }
+ else {
+ String iStr = id.getName().toString();
+ packages.add(iStr.substring(0, iStr.lastIndexOf( "." ) ));
+ }
+ }
+
+ return packages.toArray( new String[packages.size()] );
+ }
+
+ private String readPackage(MethodInvocation m) {
+ return m.resolveMethodBinding().getDeclaringClass().getPackage().getName();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
new file mode 100644
index 0000000..fbbf524
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.ui.CodeStyleConfiguration;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportSearchResultProposal implements IJavaCompletionProposal {
+
+ private ISigilProjectModel project;
+ private ICompilationUnit fCompilationUnit;
+ private final ISearchResult result;
+ private final Shell shell;
+
+ public ImportSearchResultProposal(Shell shell, ISearchResult result, Name node, ISigilProjectModel project) {
+ this.shell = shell;
+ this.result = result;
+ this.project = project;
+ if ( node != null ) {
+ CompilationUnit cu = (CompilationUnit) ASTNodes.getParent(node, ASTNode.COMPILATION_UNIT);
+ this.fCompilationUnit = (ICompilationUnit) cu.getJavaElement();
+ }
+ }
+
+ public int getRelevance() {
+ return 100;
+ }
+
+ public void apply(IDocument document) {
+ IPackageExport e = result.getExport();
+ if ( result.getExport() == null ) {
+ if ( MessageDialog.openQuestion(shell, "Modify " + result.getProvider().getBundleInfo().getSymbolicName(), result.getPackageName() + " is not exported. Do you want to export it now?" ) ) {
+ final IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pe.setPackageName(result.getPackageName());
+ //e.setVersion(version)
+ final ISigilProjectModel mod = result.getProvider().getAncestor(ISigilProjectModel.class);
+ if ( mod == null ) {
+ throw new IllegalStateException( "Attempt to modify binary package export" );
+ }
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ mod.getBundle().getBundleInfo().addExport(pe);
+ mod.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ e = pe;
+ }
+ }
+
+ final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ i.setPackageName(e.getPackageName());
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = e.getVersion();
+ VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ i.setVersions( selectedVersions );
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ project.getBundle().getBundleInfo().addImport( i );
+ project.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ addSourceImport();
+ }
+
+ private void addSourceImport() {
+ // add import
+ try {
+ ImportRewrite rewrite= CodeStyleConfiguration.createImportRewrite(fCompilationUnit, true);
+ rewrite.addImport(result.getClassName());
+ JavaModelUtil.applyEdit(fCompilationUnit, rewrite.rewriteImports(null), false, null);
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to add import", e);
+ }
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public IContextInformation getContextInformation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getDisplayString() {
+ String type = result.getClassName();
+ String loc = result.getExport() == null ? " from " + result.getProvider().getBundleInfo().getSymbolicName() : " version " + result.getExport().getVersion();
+ return "Import " + type + loc;
+ }
+
+ public Image getImage() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
new file mode 100644
index 0000000..3a264c2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class ImportedClassReference {
+ private IPackageImport pi;
+ private String type;
+
+ public ImportedClassReference(IPackageImport packageImport, String typeName) {
+ this.pi = packageImport;
+ this.type = typeName;
+ }
+
+ public IPackageImport getPackageImport() {
+ return pi;
+ }
+
+ public String getFullType() {
+ return type;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
new file mode 100644
index 0000000..ce5075f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.refactor;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+public class RenameCompositeRefactoring extends Refactoring {
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
new file mode 100644
index 0000000..3ad4fe9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public abstract class AccumulatorAdapter<E> implements IAccumulator<E> {
+ public void addElement(E element) {
+ LinkedList<E> list = new LinkedList<E>();
+ list.add(element);
+ addElements(list);
+ };
+
+ public void addElements(Collection<? extends E> elements) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
new file mode 100644
index 0000000..8ad02de
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
@@ -0,0 +1,487 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalListener;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class BackgroundLoadingSelectionDialog<E> extends TitleAreaDialog implements IAccumulator<E> {
+
+ private final ILabelProvider DEFAULT_LABEL_PROVIDER = new LabelProvider() {
+ @SuppressWarnings("unchecked")
+ public String getText(Object element) {
+ String result;
+ if(element instanceof WrappedContentProposal<?>) {
+ WrappedContentProposal<E> contentProposal = (WrappedContentProposal<E>) element;
+ result = contentProposal.getLabel();
+ } else {
+ result = descriptor.getLabel((E) element);
+ }
+ return result;
+ }
+ };
+
+ private final IElementDescriptor<E> DEFAULT_DESCRIPTOR = new IElementDescriptor<E>() {
+ public String getLabel(E element) {
+ return getName(element);
+ }
+ public String getName(E element) {
+ return element == null ? "null" : element.toString();
+ }
+ };
+
+ private final String selectionLabel;
+ private IFilter<? super E> filter;
+ private IElementDescriptor<? super E> descriptor = DEFAULT_DESCRIPTOR;
+ private ILabelProvider labelProvider = DEFAULT_LABEL_PROVIDER;
+ private final boolean multi;
+
+ private final List<E> elements;
+
+ private List<E> selection = null;
+ private String selectedName = null;
+
+ private TableViewer viewer = null;
+ private Comparator<? super E> comparator;
+
+ private HashMap<String, IJobRunnable> background = new HashMap<String, IJobRunnable>();
+
+ public BackgroundLoadingSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+ super(parentShell);
+ elements = new ArrayList<E>();
+ this.selectionLabel = selectionLabel;
+ this.multi = multi;
+ }
+
+ public void setFilter(IFilter<? super E> filter) {
+ this.filter = filter;
+ }
+
+ public void setDescriptor(final IElementDescriptor<? super E> descriptor) {
+ if(descriptor != null) {
+ this.descriptor = descriptor;
+ } else {
+ this.descriptor = DEFAULT_DESCRIPTOR;
+ }
+ }
+
+ public IElementDescriptor<? super E> getDescriptor() {
+ return descriptor;
+ }
+
+ public void setComparator(Comparator<? super E> comparator) {
+ this.comparator = comparator;
+ }
+
+ public void setLabelProvider(ILabelProvider labelProvider) {
+ if(labelProvider != null) {
+ this.labelProvider = labelProvider;
+ } else {
+ this.labelProvider = DEFAULT_LABEL_PROVIDER;
+ }
+ }
+
+ public void addBackgroundJob(String name, IJobRunnable job) {
+ background.put(name, job);
+ }
+
+ @Override
+ public int open() {
+ Job[] jobs = scheduleJobs();
+ try {
+ return super.open();
+ }
+ finally {
+ for ( Job j : jobs ) {
+ j.cancel();
+ }
+ }
+ }
+
+ private Job[] scheduleJobs() {
+ if ( background.isEmpty() ) {
+ return new Job[] {};
+ }
+ else {
+ ArrayList<Job> jobs = new ArrayList<Job>(background.size());
+ for ( Map.Entry<String, IJobRunnable> e : background.entrySet() ) {
+ final IJobRunnable run = e.getValue();
+ Job job = new Job(e.getKey()) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ return run.run(monitor);
+ }
+ };
+ job.schedule();
+ }
+
+ return jobs.toArray( new Job[jobs.size()] );
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create Controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ new Label(composite, SWT.NONE).setText(selectionLabel);
+
+ ContentProposalAdapter proposalAdapter = null;
+ Text txtSelection = null;
+
+ Table table = null;
+ if(multi) {
+ table = new Table(composite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
+ viewer = new TableViewer(table);
+ viewer.setContentProvider(new ArrayContentProvider());
+ viewer.addFilter(new ViewerFilter() {
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ @SuppressWarnings("unchecked") E castedElement = (E) element;
+ return filter == null || filter.select(castedElement);
+ }
+ });
+ if(comparator != null) {
+ viewer.setSorter(new ViewerSorter() {
+ @Override
+ public int compare(Viewer viewer, Object o1, Object o2) {
+ @SuppressWarnings("unchecked")
+ E e1 = (E) o1;
+ @SuppressWarnings("unchecked")
+ E e2 = (E) o2;
+ return comparator.compare(e1, e2);
+ }
+ });
+ }
+ synchronized (elements) {
+ viewer.setInput(elements);
+ }
+
+ if(labelProvider != null) {
+ viewer.setLabelProvider(labelProvider);
+ }
+ } else {
+ txtSelection = new Text(composite, SWT.BORDER);
+ ControlDecoration selectionDecor = new ControlDecoration(txtSelection, SWT.LEFT | SWT.TOP);
+ FieldDecoration proposalDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
+ selectionDecor.setImage(proposalDecor.getImage());
+ selectionDecor.setDescriptionText(proposalDecor.getDescription());
+
+ ExclusionContentProposalProvider<E> proposalProvider = new ExclusionContentProposalProvider<E>(elements, filter, descriptor);
+
+ proposalAdapter = new ContentProposalAdapter(txtSelection, new TextContentAdapter(), proposalProvider, null, null);
+ proposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+ if(labelProvider != null) {
+ proposalAdapter.setLabelProvider(labelProvider);
+ }
+
+ if(selectedName != null) {
+ txtSelection.setText(selectedName);
+ }
+ }
+ updateSelection();
+ updateButtons();
+
+ // Hookup listeners
+ if(proposalAdapter != null) {
+ proposalAdapter.addContentProposalListener(new IContentProposalListener() {
+ public void proposalAccepted(IContentProposal proposal) {
+ @SuppressWarnings("unchecked")
+ WrappedContentProposal<E> valueProposal = (WrappedContentProposal<E>) proposal;
+ E selected = valueProposal.getElement();
+ selection = new ArrayList<E>(1);
+ selection.add(selected);
+
+ elementSelected(selected);
+
+ updateButtons();
+ }
+ });
+ }
+ if(txtSelection != null) {
+ txtSelection.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ selectedName = ((Text) e.widget).getText();
+ updateButtons();
+ }
+ });
+ }
+ if(viewer != null) {
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ selection = new ArrayList<E>(sel.size());
+ for(Iterator<?> iter = sel.iterator(); iter.hasNext(); ) {
+ @SuppressWarnings("unchecked")
+ E element = (E) iter.next();
+ selection.add(element);
+ }
+ updateButtons();
+ }
+ });
+ viewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ if(canComplete()) {
+ setReturnCode(IDialogConstants.OK_ID);
+ close();
+ }
+ }
+ });
+ }
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ if(multi) {
+ composite.setLayout(new GridLayout(1, false));
+ GridData layoutTable = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutTable.heightHint = 200;
+ table.setLayoutData(layoutTable);
+ } else {
+ composite.setLayout(new GridLayout(2, false));
+ txtSelection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ }
+
+ return container;
+ }
+
+ protected void elementSelected(E selection) {
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent) {
+ Control bar = super.createButtonBar(parent);
+ updateButtons();
+ return bar;
+ }
+
+ /**
+ * Can be called from any thread
+ */
+ protected final void updateButtons() {
+ Runnable updateButtonsRunnable = new Runnable() {
+ public void run() {
+ Shell shell = getShell();
+ if(shell != null && !shell.isDisposed()) {
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ if(okButton != null && !okButton.isDisposed()) {
+ okButton.setEnabled(canComplete());
+ }
+ }
+ }
+ };
+ Shell shell = getShell();
+ if (shell != null) {
+ onUIThread(shell, updateButtonsRunnable);
+ }
+ }
+
+ /**
+ * Subclasses may override but must call super.canComplete
+ * @return
+ */
+ protected synchronized boolean canComplete() {
+ boolean result = false;
+
+ if ( selection != null ) {
+ if(multi) {
+ result = selection.size() > 0;
+ } else {
+ E sel = getSelectedElement();
+ result = sel != null && descriptor.getName(sel).equals(selectedName);
+ }
+ }
+
+ return result;
+ }
+
+ public final void addElement(E added) {
+ addElements(Collections.singleton(added));
+ }
+
+ /**
+ * Can be called from any thread
+ */
+ public final void addElements(Collection<? extends E> added) {
+ final LinkedList<E> toAdd = new LinkedList<E>();
+ synchronized (elements) {
+ for ( E e : added ) {
+ if ( !elements.contains(e) ) {
+ elements.add( e );
+ toAdd.add(e);
+ }
+ }
+ Collections.sort(elements, comparator);
+ }
+ if(viewer != null) {
+ onUIThread(viewer.getControl(), new Runnable() {
+ public void run() {
+ if(!viewer.getControl().isDisposed()) {
+ viewer.add( toAdd.toArray() );
+ viewer.refresh();
+ }
+ }
+ });
+ }
+ else {
+
+ }
+ updateSelection();
+ updateButtons();
+ }
+
+ protected void updateSelection() {
+ onUIThread(getShell(), new Runnable() {
+ public void run() {
+ if(selectedName != null) {
+ ArrayList<E> newSelection = new ArrayList<E>();
+ synchronized (elements) {
+ for (E e : elements) {
+ if(selectedName.equals(descriptor.getName(e))) {
+ newSelection.add(e);
+ break;
+ }
+ }
+ }
+ selection = newSelection;
+ }
+ else {
+ selection = Collections.emptyList();
+ }
+ if(viewer != null && !viewer.getControl().isDisposed()) {
+ viewer.setSelection(selection.isEmpty() ? StructuredSelection.EMPTY : new StructuredSelection(selection));
+ }
+ }
+ });
+ }
+
+ private static final void onUIThread(Control control, Runnable r) {
+ if(control != null && !control.isDisposed()) {
+ try {
+ Display display = control.getDisplay();
+ if(Thread.currentThread() == display.getThread()) {
+ // We are on the UI thread already, just do the work
+ r.run();
+ } else {
+ // Not on the UI thread, need to bung over the runnable
+ display.asyncExec(r);
+ }
+ }
+ catch (SWTError e) {
+ if ( e.code == SWT.ERROR_WIDGET_DISPOSED ) {
+ // ignore
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ public String getSelectedName() {
+ return selectedName;
+ }
+
+ public void setSelectedName(String selectedName) {
+ this.selectedName = selectedName;
+ boolean change = false;
+ if ( selectedName == null ) {
+ if ( selection != null && !selection.isEmpty() ) {
+ change = true;
+ }
+ }
+ else {
+ if ( selection == null ) {
+ change = true;
+ }
+ else if ( selection.size() != 1 || !descriptor.getLabel(selection.get(0)).equals(selectedName)) {
+ change = true;
+ }
+ }
+
+ if ( change ) {
+ updateSelection();
+ updateButtons();
+ }
+ }
+
+ public List<E> getSelectedElements() {
+ return selection;
+ }
+
+ public E getSelectedElement() {
+ E result;
+ if(selection == null || selection.isEmpty()) {
+ result = null;
+ } else {
+ result = selection.get(0);
+ }
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
new file mode 100644
index 0000000..2febfbd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class ColumnModelLabelProvider extends ColumnLabelProvider {
+
+ private ModelLabelProvider provider = new ModelLabelProvider();
+
+ @Override
+ public Image getImage(Object element) {
+ return provider.getImage(element);
+ }
+
+ @Override
+ public String getText(Object element) {
+ return provider.getText(element);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
new file mode 100644
index 0000000..5fb4237
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class DefaultContentProvider implements IContentProvider {
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
new file mode 100644
index 0000000..29fb24b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+
+public abstract class DefaultLabelProvider implements IBaseLabelProvider, ILabelProvider {
+
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ public void dispose() {
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
new file mode 100644
index 0000000..715afa9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+
+public abstract class DefaultTableProvider extends DefaultContentProvider implements IStructuredContentProvider {
+
+ /**
+ * Utility method to convert the input element to an Object[].
+ *
+ * @param inputElement
+ *
+ * @return if inputElement is null -> empty array <br/>
+ * if inputElement is a {@link Collection} returns {@link Collection#toArray()}<br/>
+ * if inputElement is an Array class cast of inputElement to Object[]<br/>
+ *
+ * @throws IllegalArgumentException if the element cannot be converted.
+ */
+ public Object[] toArray(Object inputElement) {
+ if ( inputElement == null ) {
+ return new Object[] {};
+ }
+ else if ( inputElement instanceof Collection ) {
+ Collection<?> col = (Collection<?>) inputElement;
+ return col.toArray();
+ }
+ else if ( inputElement.getClass().isArray() ) {
+ return (Object[]) inputElement;
+ }
+ else {
+ throw new IllegalArgumentException( "Invalid inputElement " + inputElement.getClass() );
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
new file mode 100644
index 0000000..d223835
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+
+public abstract class DefaultTreeContentProvider extends DefaultContentProvider implements ITreeContentProvider {
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
new file mode 100644
index 0000000..1663e2f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+public class ExclusionContentProposalProvider<T> implements
+ IContentProposalProvider {
+
+ private final Collection<? extends T> elements;
+ private final IFilter<? super T> filter;
+ private final IElementDescriptor<? super T> descriptor;
+
+ public ExclusionContentProposalProvider(Collection<? extends T> elements, IFilter<? super T> filter, IElementDescriptor<? super T> descriptor) {
+ this.elements = elements;
+ this.filter = filter;
+ this.descriptor = descriptor;
+ }
+
+ public IContentProposal[] getProposals(String contents, int position) {
+ String matchString = contents.substring(0, position);
+ Pattern pattern = GlobCompiler.compile(matchString);
+
+ // Get a snapshot of the elements
+ T[] elementArray;
+ synchronized (elements) {
+ @SuppressWarnings("unchecked") T[] temp = (T[]) elements.toArray();
+ elementArray = temp;
+ }
+
+ List<IContentProposal> result = new ArrayList<IContentProposal>();
+
+ for (T element : elementArray) {
+ if(filter != null && filter.select(element)) {
+ IContentProposal proposal = WrappedContentProposal.newInstance(element, descriptor);
+ Matcher matcher = pattern.matcher(proposal.getContent());
+ if(matcher.find()) {
+ result.add(proposal);
+ }
+ }
+ }
+
+ return result.toArray(new IContentProposal[result.size()]);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
new file mode 100644
index 0000000..5c85724
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ExportedPackageFinder implements IJobRunnable {
+
+ private final IAccumulator<? super IPackageExport> accumulator;
+ private final ISigilProjectModel sigil;
+
+ public ExportedPackageFinder(ISigilProjectModel sigil, IAccumulator<? super IPackageExport> accumulator) {
+ this.sigil = sigil;
+ this.accumulator = accumulator;
+ }
+
+ public IStatus run(final IProgressMonitor monitor) {
+ final List<IPackageExport> exports = new ArrayList<IPackageExport>(ResourcesDialogHelper.UPDATE_BATCH_SIZE);
+ final IModelWalker walker = new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pkgExport = (IPackageExport) element;
+ exports.add(pkgExport);
+
+ if(exports.size() >= ResourcesDialogHelper.UPDATE_BATCH_SIZE) {
+ accumulator.addElements(exports);
+ exports.clear();
+ }
+ }
+ return !monitor.isCanceled();
+ }
+ };
+ SigilCore.getRepositoryManager(sigil).visit(walker);
+ if(exports.size() > 0) {
+ accumulator.addElements(exports);
+ }
+
+ return Status.OK_STATUS;
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
new file mode 100644
index 0000000..60cb121
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class FileUtils {
+ public static void loadFile( Shell shell, Text text, String msg, boolean isDirectory ) {
+ if ( isDirectory ) {
+ DirectoryDialog dialog = new DirectoryDialog(shell, SWT.NONE);
+ dialog.setMessage(msg);
+ String value = dialog.open();
+ if ( value != null ) {
+ text.setText( value );
+ }
+ }
+ else {
+ FileDialog dialog = new FileDialog(shell, SWT.NONE);
+ dialog.setText(msg);
+ String value = dialog.open();
+ if ( value != null ) {
+ text.setText( value );
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
new file mode 100644
index 0000000..99384be
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+public interface IAccumulator<E> {
+ public void addElement(E element);
+ public void addElements(Collection<? extends E> elements);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
new file mode 100644
index 0000000..915d16c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IExportToImportConverter<E,I> {
+ I convert(E exportElement);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
new file mode 100644
index 0000000..6cd3f23
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IFilter<T> {
+ boolean select(T element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
new file mode 100644
index 0000000..6c1b701
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IValidationListener {
+
+ void validationMessage(String message, int level);
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
new file mode 100644
index 0000000..6ff6901
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Version;
+
+public class ModelLabelProvider extends LabelProvider {
+
+ private volatile Set<? extends IModelElement> unresolvedElements = null;
+
+ public Image getImage(Object element) {
+ boolean unresolved = (unresolvedElements == null) ? false : unresolvedElements.contains(element);
+
+ if ( element instanceof ISigilBundle || element instanceof IBundleModelElement) {
+ return findBundle();
+ } else if(element instanceof IRequiredBundle) {
+ boolean optional = ((IRequiredBundle) element).isOptional();
+ return findRequiredBundle(optional, unresolved);
+ }
+ else if ( element instanceof IPackageImport ) {
+ boolean optional = ((IPackageImport) element).isOptional();
+ return findPackageImport(optional, unresolved);
+ }
+ else if ( element instanceof IPackageExport ) {
+ return findPackageExport();
+ }
+ else if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+ try {
+ if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+ return findPackage();
+ }
+ else {
+ return findBundle();
+ }
+ } catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect package fragment root", e );
+ }
+ }
+ else if ( element instanceof IClasspathEntry ) {
+ return findPackage();
+ }
+ if ( element instanceof IBundleRepository ) {
+ IBundleRepository rep = (IBundleRepository) element;
+ IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ return config.getType().getIcon();
+ }
+
+ return null;
+ }
+
+ public String getText(Object element) {
+ if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ return bundle.getBundleInfo().getSymbolicName() + " " + bundle.getBundleInfo().getVersion();
+ }
+ if ( element instanceof IBundleModelElement ) {
+ IBundleModelElement bundle = (IBundleModelElement) element;
+ return bundle.getSymbolicName();
+ }
+ if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle req = (IRequiredBundle) element;
+ return req.getSymbolicName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageImport ) {
+ IPackageImport req = (IPackageImport) element;
+ return req.getPackageName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) element;
+ Version rawVersion = pe.getRawVersion();
+ return rawVersion != null ? pe.getPackageName() + " " + rawVersion : pe.getPackageName();
+ }
+
+ if ( element instanceof IResource ) {
+ IResource resource = (IResource) element;
+ return resource.getName();
+ }
+
+ if ( element instanceof IPackageFragment ) {
+ IPackageFragment f = (IPackageFragment) element;
+ return f.getElementName();
+ }
+
+ if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+ try {
+ return f.getUnderlyingResource().getName();
+ } catch (JavaModelException e) {
+ return "unknown";
+ }
+ }
+
+ if ( element instanceof IClasspathEntry ) {
+ IClasspathEntry cp = (IClasspathEntry) element;
+ return cp.getPath().toString();
+ }
+
+ if ( element instanceof IBundleRepository ) {
+ IBundleRepository rep = (IBundleRepository) element;
+ IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ return config.getName();
+ }
+
+ return element.toString();
+ }
+
+ private Image findPackage() {
+ return cacheImage( "icons/package_obj.png" );
+ }
+
+ private Image findPackageImport(boolean optional, boolean unresolved) {
+ String path;
+ if(optional) {
+ path = unresolved ? "icons/package_obj_import_opt_error.png" : "icons/package_obj_import_opt.png";
+ } else {
+ path = unresolved ? "icons/package_obj_import_error.png" : "icons/package_obj_import.png";
+ }
+ return cacheImage(path);
+ }
+
+ private Image findPackageExport() {
+ return cacheImage("icons/package_obj_export.png");
+ }
+
+ private Image findBundle() {
+ return cacheImage("icons/jar_obj.png");
+ }
+
+ private Image findRequiredBundle(boolean optional, boolean unresolved) {
+ String path;
+ if(optional) {
+ path = unresolved ? "icons/required_bundle_opt_error.png" : "icons/required_bundle_opt.png";
+ } else {
+ path = unresolved ? "icons/required_bundle_error.png" : "icons/jar_obj.png";
+ }
+ return cacheImage(path);
+ }
+
+ private static Image cacheImage(String path) {
+ return SigilUI.cacheImage(path, ModelLabelProvider.class.getClassLoader());
+ }
+
+ public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+ this.unresolvedElements = elements;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
new file mode 100644
index 0000000..d451538
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class PackageFilter implements IFilter<IPackageImport> {
+
+ private Set<String> names = new HashSet<String>();
+
+ public PackageFilter(String[] packageNames) {
+ for (String name : packageNames) {
+ names.add(name);
+ }
+ }
+
+ public PackageFilter(IPackageExport[] packages) {
+ for (IPackageExport packageExport : packages) {
+ names.add(packageExport.getPackageName());
+ }
+ }
+
+ public boolean select(IPackageImport element) {
+ return !names.contains(element.getPackageName());
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
new file mode 100644
index 0000000..4cf8ad4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ProjectUtils {
+ public static boolean runTaskWithRebuildCheck(final Runnable task, Shell shell) {
+ return runTaskWithRebuildCheck( new Callable<Boolean>() {
+ public Boolean call() throws Exception {
+ task.run();
+ return true;
+ }
+ }, shell);
+ }
+
+ public static boolean runTaskWithRebuildCheck(Callable<Boolean> callable,
+ Shell shell) {
+ int result = checkRebuild(shell);
+ if ( result == IDialogConstants.CANCEL_ID ) {
+ return false;
+ }
+ else {
+ try {
+ if ( Boolean.TRUE == callable.call() ) {
+ if ( result == IDialogConstants.YES_ID ) {
+ SigilUI.runWorkspaceOperation( new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) {
+ SigilCore.rebuildAllBundleDependencies(monitor);
+ }
+ }, shell );
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+ } catch (Exception e) {
+ SigilCore.error( "Failed to run caller", e);
+ return false;
+ }
+ }
+ }
+
+ private static int checkRebuild(Shell shell) {
+ if ( SigilCore.getRoot().getProjects().isEmpty() ) {
+ return IDialogConstants.NO_ID;
+ }
+ else {
+ return OptionalPrompt.optionallyPromptWithCancel(SigilCore.getDefault().getPreferenceStore(), SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild", "Do you wish to rebuild all Sigil projects", shell );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
new file mode 100644
index 0000000..bebe867
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class ResourceReviewDialog<T extends IModelElement> extends TitleAreaDialog {
+
+ private String title;
+ private Collection<T> resources;
+
+ private TableViewer viewer;
+
+ public ResourceReviewDialog(Shell parentShell, String title, Collection<T> resources) {
+ super(parentShell);
+ this.title = title;
+ this.resources = resources;
+ }
+
+ public Collection<T> getResources() {
+ return resources;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(title);
+
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+ Table table = new Table(composite, SWT.BORDER | SWT.VIRTUAL);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ } );
+
+ viewer = new TableViewer(table);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setInput( resources );
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ remove.setEnabled(!event.getSelection().isEmpty());
+ }
+ });
+
+ // layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ composite.setLayout(new GridLayout(2, false));
+ GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4);
+ tableLayoutData.heightHint = 150;
+ table.setLayoutData(tableLayoutData);
+
+ return container;
+ }
+
+ private void handleRemove() {
+ ISelection s = viewer.getSelection();
+ if ( !s.isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) s;
+ resources.remove( sel.getFirstElement() );
+ viewer.refresh();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
new file mode 100644
index 0000000..4644fe3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.NewPackageExportDialog;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ResourcesDialogHelper {
+
+ static final int UPDATE_BATCH_SIZE = 100;
+
+ public static BackgroundLoadingSelectionDialog<String> createClassSelectDialog(Shell shell, String title, final ISigilProjectModel project, String selected, final String ifaceOrParentClass) {
+ final BackgroundLoadingSelectionDialog<String> dialog = new BackgroundLoadingSelectionDialog<String>(shell, "Class Name", true);
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(IProgressMonitor monitor) {
+ try {
+ for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT ) ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+ if ( project.isInBundleClasspath(root)) {
+ for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.COMPILATION_UNIT, IJavaElement.CLASS_FILE ) ) {
+ ITypeRoot typeRoot = (ITypeRoot) e1;
+ IType type = (IType) JavaHelper.findType(typeRoot, IJavaElement.TYPE);
+ if ( JavaHelper.isAssignableTo( ifaceOrParentClass, type ) ) {
+ dialog.addElement(type.getFullyQualifiedName());
+ }
+ }
+ }
+ }
+
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return e.getStatus();
+ }
+ }
+
+ };
+
+ dialog.addBackgroundJob("Scanning for activators in project", job);
+
+ return dialog;
+ }
+
+ public static NewResourceSelectionDialog<IPackageExport> createImportDialog(
+ Shell shell,
+ String title,
+ ISigilProjectModel sigil,
+ final IPackageImport selected,
+ final Collection<IPackageImport> existing) {
+ final Set<String> existingNames = new HashSet<String>();
+
+ for (IPackageImport existingImport : existing) {
+ existingNames.add(existingImport.getPackageName());
+ }
+
+ final NewResourceSelectionDialog<IPackageExport> dialog = new NewResourceSelectionDialog<IPackageExport>(shell, "Package Name:", false);
+
+ dialog.setFilter( new IFilter<IPackageModelElement>() {
+ public boolean select(IPackageModelElement element) {
+ return !existingNames.contains(element.getPackageName());
+ }
+ } );
+
+ dialog.setComparator(new Comparator<IPackageExport>() {
+ public int compare(IPackageExport o1, IPackageExport o2) {
+ return o1.compareTo(o2);
+ }
+ });
+
+ dialog.setDescriptor(new IElementDescriptor<IPackageExport>() {
+ public String getLabel(IPackageExport element) {
+ return getName(element) + " (" + element.getVersion().toString() + ")";
+ }
+
+ public String getName(IPackageExport element) {
+ return element.getPackageName();
+ }
+ });
+
+ dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IPackageExport>(dialog.getDescriptor()));
+
+ if(selected != null) {
+ dialog.setSelectedName(selected.getPackageName());
+ dialog.setVersions(selected.getVersions());
+ dialog.setOptional(selected.isOptional());
+ }
+
+ IJobRunnable job = new ExportedPackageFinder(sigil, dialog);
+ dialog.addBackgroundJob("Scanning for exports in workspace", job);
+
+ return dialog;
+ }
+
+ public static NewPackageExportDialog createNewExportDialog(Shell shell, String title, final IPackageExport selected, final ISigilProjectModel project, boolean multiSelect) {
+ IFilter<IJavaElement> selectFilter = new IFilter<IJavaElement>() {
+ public boolean select(IJavaElement e) {
+ if ( selected != null && e.getElementName().equals( selected.getPackageName() ) ) {
+ return true;
+ }
+
+ if ( e.getElementName().trim().length() > 0 && isLocal( e ) ) {
+ for ( IPackageExport p : project.getBundle().getBundleInfo().getExports() ) {
+ if ( p.getPackageName().equals( e.getElementName() ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean isLocal(IJavaElement java) {
+ try {
+ switch ( java.getElementType() ) {
+ case IJavaElement.PACKAGE_FRAGMENT:
+ IPackageFragment fragment= (IPackageFragment) java;
+ return fragment.containsJavaResources();
+ default:
+ throw new IllegalStateException( "Unexpected resource type " + java );
+ }
+ }
+ catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect java element ", e );
+ return false;
+ }
+ }
+
+ };
+
+ final NewPackageExportDialog dialog = new NewPackageExportDialog(shell, multiSelect);
+ dialog.setFilter(selectFilter);
+
+ dialog.setProjectVersion(project.getVersion());
+ if ( selected != null ) {
+ dialog.setSelectedName(selected.getPackageName());
+ dialog.setVersion(selected.getRawVersion());
+ }
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(IProgressMonitor monitor) {
+ try {
+ ArrayList<IPackageFragment> list = new ArrayList<IPackageFragment>(UPDATE_BATCH_SIZE);
+ for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT) ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+ if ( project.isInBundleClasspath(root) ) {
+ for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.PACKAGE_FRAGMENT) ) {
+ list.add((IPackageFragment) e1);
+ if(list.size() >= UPDATE_BATCH_SIZE) {
+ dialog.addElements(list);
+ list.clear();
+ }
+ }
+ }
+ }
+ if(!list.isEmpty()) {
+ dialog.addElements(list);
+ }
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return e.getStatus();
+ }
+ }
+ };
+
+ dialog.addBackgroundJob("Scanning for packages in project", job);
+
+ return dialog;
+ }
+
+ public static NewResourceSelectionDialog<IBundleModelElement> createRequiredBundleDialog(Shell shell, String title, final ISigilProjectModel sigil, final IRequiredBundle selected, final Collection<IRequiredBundle> existing) {
+ final Set<String> existingNames = new HashSet<String>();
+ for(IRequiredBundle existingBundle : existing) {
+ existingNames.add(existingBundle.getSymbolicName());
+ }
+
+ final NewResourceSelectionDialog<IBundleModelElement> dialog = new NewResourceSelectionDialog<IBundleModelElement>(shell, "Bundle:", false);
+
+ dialog.setDescriptor(new IElementDescriptor<IBundleModelElement>() {
+ public String getLabel(IBundleModelElement element) {
+ return getName(element) + " (" + element.getVersion() + ")";
+ }
+ public String getName(IBundleModelElement element) {
+ return element.getSymbolicName();
+ }
+ });
+
+ dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IBundleModelElement>(dialog.getDescriptor()));
+
+ dialog.setFilter(new IFilter<IBundleModelElement>() {
+ public boolean select(IBundleModelElement element) {
+ return !existingNames.contains(element.getSymbolicName());
+ }
+ });
+
+ dialog.setComparator(new Comparator<IBundleModelElement>() {
+ public int compare(IBundleModelElement o1, IBundleModelElement o2) {
+ return o1.getSymbolicName().compareTo(o2.getSymbolicName());
+ }
+ });
+
+ if(selected != null) {
+ dialog.setSelectedName(selected.getSymbolicName());
+ dialog.setVersions(selected.getVersions());
+ dialog.setOptional(selected.isOptional());
+ }
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(final IProgressMonitor monitor) {
+ final List<IBundleModelElement> bundles = new ArrayList<IBundleModelElement>(UPDATE_BATCH_SIZE);
+ final IModelWalker walker = new IModelWalker() {
+ //int count = 0;
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IBundleModelElement) {
+ IBundleModelElement b = (IBundleModelElement) element;
+ bundles.add(b);
+
+ if(bundles.size() >= UPDATE_BATCH_SIZE) {
+ dialog.addElements(bundles);
+ bundles.clear();
+ }
+ // no need to recurse further.
+ return false;
+ }
+ return !monitor.isCanceled();
+ }
+ };
+ SigilCore.getRepositoryManager(sigil).visit(walker);
+ if(!bundles.isEmpty()) {
+ dialog.addElements(bundles);
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ dialog.addBackgroundJob("Scanning for bundles in workspace", job);
+
+ return dialog;
+ }
+}
+
+
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
new file mode 100644
index 0000000..22a0a46
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+@SuppressWarnings("unchecked")
+public class SingletonSelection implements IStructuredSelection {
+
+ private final Object singleton;
+
+ public SingletonSelection(Object singleton) {
+ this.singleton = singleton;
+ }
+
+ public Object getFirstElement() {
+ return singleton;
+ }
+
+ public Iterator iterator() {
+ return Collections.singleton(singleton).iterator();
+ }
+
+ public int size() {
+ return 1;
+ }
+
+ public Object[] toArray() {
+ return new Object[] { singleton };
+ }
+
+ public List toList() {
+ ArrayList list = new ArrayList(1);
+ list.add( singleton );
+ return list;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
new file mode 100644
index 0000000..f46b4b2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class WrappedContentProposalLabelProvider<E> extends LabelProvider {
+
+ private final IElementDescriptor<? super E> descriptor;
+ private final ModelLabelProvider projectLabelProvider;
+
+ public WrappedContentProposalLabelProvider(IElementDescriptor<? super E> descriptor) {
+ this.descriptor = descriptor;
+ projectLabelProvider = new ModelLabelProvider();
+ }
+
+ @SuppressWarnings("unchecked")
+ private E adapt(Object element) {
+ E result;
+ if(element instanceof WrappedContentProposal<?>) {
+ WrappedContentProposal<?> proposal = (WrappedContentProposal<?>) element;
+ result = (E) proposal.getElement();
+ } else {
+ result = (E) element;
+ }
+ return result;
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Object value = adapt(element);
+ return projectLabelProvider.getImage(value);
+ }
+
+ @Override
+ public String getText(Object element) {
+ return descriptor.getLabel(adapt(element));
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
new file mode 100644
index 0000000..cbfbbbe
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+public class ModelElementComparator extends ViewerComparator {
+ private static final int EXPORT_GROUP = 0;
+ private static final int IMPORT_GROUP = 1;
+ private static final int REQUIRE_GROUP = 2;
+ private static final int OTHER_GROUP = 4;
+ public int category(Object element) {
+ if ( element instanceof IPackageImport ) {
+ return IMPORT_GROUP;
+ }
+ else if ( element instanceof IPackageExport ) {
+ return EXPORT_GROUP;
+ }
+ else if ( element instanceof IRequiredBundle ) {
+ return REQUIRE_GROUP;
+ }
+ else {
+ return OTHER_GROUP;
+ }
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ int cat1 = category(e1);
+ int cat2 = category(e2);
+
+ if (cat1 != cat2) {
+ return cat1 - cat2;
+ }
+
+ if ( cat1 == OTHER_GROUP ) {
+ String name1;
+ String name2;
+
+ if (viewer == null || !(viewer instanceof ContentViewer)) {
+ name1 = e1.toString();
+ name2 = e2.toString();
+ } else {
+ IBaseLabelProvider prov = ((ContentViewer) viewer)
+ .getLabelProvider();
+ if (prov instanceof ILabelProvider) {
+ ILabelProvider lprov = (ILabelProvider) prov;
+ name1 = lprov.getText(e1);
+ name2 = lprov.getText(e2);
+ } else {
+ name1 = e1.toString();
+ name2 = e2.toString();
+ }
+ }
+ if (name1 == null) {
+ name1 = "";//$NON-NLS-1$
+ }
+ if (name2 == null) {
+ name2 = "";//$NON-NLS-1$
+ }
+
+ // use the comparator to compare the strings
+ return getComparator().compare(name1, name2);
+ }
+ else {
+ Comparable c1 = (Comparable) e1;
+ Comparable c2 = (Comparable) e2;
+ return c1.compareTo(c2);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
new file mode 100644
index 0000000..4fdb238
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.util.ModelHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.LocalSelectionTransfer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSourceAdapter;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.part.ViewPart;
+
+public class RepositoryViewPart extends ViewPart implements IRepositoryChangeListener {
+
+ public class FindUsersAction extends Action {
+ @Override
+ public String getText() {
+ return "Find Uses";
+ }
+
+ @Override
+ public void run() {
+ ISelection s = treeViewer.getSelection();
+ if ( !s.isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) s;
+ IModelElement e = (IModelElement) sel.getFirstElement();
+ List<IModelElement> users = ModelHelper.findUsers(e);
+ String msg = null;
+ if ( users.isEmpty() ) {
+ msg = "No users of " + e;
+ }
+ else {
+ StringBuilder b = new StringBuilder();
+ for ( IModelElement u : users ) {
+ ISigilBundle bndl = u.getAncestor(ISigilBundle.class);
+ b.append( bndl );
+ b.append( "->" );
+ b.append( u );
+ b.append( "\n" );
+ }
+ msg = b.toString();
+ }
+ MessageDialog.openInformation(getViewSite().getShell(), "Information", msg );
+ }
+ }
+ }
+
+ class RepositoryAction extends Action {
+ final IBundleRepository rep;
+ final IRepositoryModel model;
+
+ public RepositoryAction(IBundleRepository rep) {
+ this.rep = rep;
+ this.model = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ }
+
+
+ @Override
+ public void run() {
+ treeViewer.setInput(rep);
+ createMenu();
+ }
+
+ @Override
+ public String getText() {
+ String name = model.getName();
+ if ( treeViewer.getInput() == rep ) {
+ name = "> " + name;
+ }
+ return name;
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ Image img = model.getType().getIcon();
+ if ( img == null ) {
+ return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/jars_obj.png");
+ }
+ else {
+ return ImageDescriptor.createFromImage(img);
+ }
+ }
+ }
+
+ class RefreshAction extends Action {
+ @Override
+ public void run() {
+ IBundleRepository rep = (IBundleRepository) treeViewer.getInput();
+ if ( rep != null ) {
+ rep.refresh();
+ treeViewer.refresh();
+ }
+ }
+
+ @Override
+ public String getText() {
+ return "Refresh";
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/refreshBundle.png");
+ }
+
+ }
+
+ private TreeViewer treeViewer;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ createBody(parent);
+ createMenu();
+ SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ SigilCore.getGlobalRepositoryManager().removeRepositoryChangeListener(this);
+ super.dispose();
+ }
+
+ private void createMenu() {
+ createTopMenu();
+ createLocalMenu();
+ }
+
+ private void createLocalMenu() {
+ /*MenuManager menuMgr = new MenuManager();
+ menuMgr.add( new FindUsersAction() );
+ Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
+
+ treeViewer.getControl().setMenu(menu);
+ getViewSite().registerContextMenu(menuMgr, treeViewer); */
+ IActionBars bars = getViewSite().getActionBars();
+ IToolBarManager toolBar = bars.getToolBarManager();
+ toolBar.add( new RefreshAction() );
+ }
+
+ private void createTopMenu() {
+ IActionBars bars = getViewSite().getActionBars();
+ IMenuManager menu = bars.getMenuManager();
+ menu.removeAll();
+ for ( final IBundleRepository rep : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+ if ( treeViewer.getInput() == null ) {
+ treeViewer.setInput(rep);
+ }
+
+ RepositoryAction action = new RepositoryAction(rep);
+ menu.add( action );
+ }
+ }
+
+ private void createBody(Composite parent) {
+ // components
+ Composite control = new Composite(parent, SWT.NONE);
+ Tree tree = new Tree(control, SWT.NONE);
+
+ // layout
+ control.setLayout( new GridLayout(1, false ) );
+ tree.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1) );
+
+ // viewer
+ treeViewer = new TreeViewer(tree);
+ treeViewer.setContentProvider( new DefaultTreeContentProvider() {
+ public Object[] getChildren(Object parentElement) {
+ if ( parentElement instanceof ICompoundModelElement ) {
+ ICompoundModelElement model = (ICompoundModelElement) parentElement;
+ return model.children();
+ }
+
+ return null;
+ }
+
+ public Object getParent(Object element) {
+ if ( element instanceof IModelElement ) {
+ IModelElement model = (IModelElement) element;
+ return model.getParent();
+ }
+
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if ( element instanceof ICompoundModelElement ) {
+ ICompoundModelElement model = (ICompoundModelElement) element;
+ return model.children().length > 0;
+ }
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ IBundleRepository rep = (IBundleRepository) inputElement;
+ return getBundles(rep);
+ }
+ });
+
+ treeViewer.setComparator( new ModelElementComparator() );
+
+ treeViewer.setLabelProvider( new ModelLabelProvider() );
+
+ treeViewer.addDragSupport(DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DragSourceAdapter() {
+ @Override
+ public void dragFinished(DragSourceEvent event) {
+ // TODO Auto-generated method stub
+ super.dragFinished(event);
+ }
+
+ @Override
+ public void dragSetData(DragSourceEvent event) {
+ // TODO Auto-generated method stub
+ super.dragSetData(event);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void dragStart(DragSourceEvent event) {
+ if ( treeViewer.getSelection().isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) treeViewer.getSelection();
+ for ( Iterator<IModelElement> i = sel.iterator(); i.hasNext(); ) {
+ IModelElement e = i.next();
+ if ( e instanceof ISigilBundle ) {
+ event.data = e;
+ }
+ else {
+ event.doit = false;
+ }
+ }
+ }
+ else {
+ event.doit = false;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ public void repositoryChanged(RepositoryChangeEvent event) {
+ switch ( event.getType() ) {
+ case ADDED:
+ createTopMenu();
+ break;
+ case CHANGED:
+ if ( event.getRepository() == treeViewer.getInput() ) {
+ SigilUI.runInUI( new Runnable() {
+ public void run() {
+ treeViewer.refresh();
+ }
+ } );
+ }
+ break;
+ case REMOVED:
+ if ( event.getRepository() == treeViewer.getInput() ) {
+ treeViewer.setInput(null);
+ }
+ createTopMenu();
+ }
+ }
+
+ private Object[] getBundles(IBundleRepository repository) {
+ final LinkedList<ISigilBundle> bundles = new LinkedList<ISigilBundle>();
+ repository.accept(new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ bundles.add(bundle);
+ return true;
+ }
+ });
+ return bundles.toArray();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
new file mode 100644
index 0000000..586375e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.core.widgets.GraphItem;
+import org.eclipse.zest.core.widgets.GraphNode;
+
+public class BundleConnectionHighlighter implements ISelectionChangedListener {
+
+ private BundleResolverView view;
+
+ public BundleConnectionHighlighter(BundleResolverView view) {
+ this.view = view;
+ }
+
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection selection = event.getSelection();
+ if ( !selection.isEmpty() ) {
+ IStructuredSelection str = (IStructuredSelection) selection;
+
+ Object sel = str.getFirstElement();
+
+ if ( sel instanceof ISigilBundle ) {
+ BundleGraph graph = (BundleGraph) view.getBundlegraph();
+
+ ISigilBundle selected = (ISigilBundle) sel;
+ Set<ISigilBundle> connected = graph.getTargets(selected);
+
+ highlightLinks(graph, selected, connected);
+ highlightBundles(graph, selected, connected);
+ }
+ else if ( sel instanceof Link ) {
+ GraphConnection c = (GraphConnection) findGraphItem(sel);
+ if ( c != null ) {
+ c.unhighlight();
+ c.setHighlightColor(ColorConstants.blue);
+ c.highlight();
+ }
+ }
+ }
+ else {
+ // TODO clear highlights...
+ }
+ }
+
+ private void highlightBundles(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+ for ( ISigilBundle bundle : graph.getBundles() ) {
+ GraphNode node = (GraphNode) findGraphItem(bundle);
+ if ( node != null ) {
+ node.unhighlight();
+
+ if ( bundle == selected ) {
+ node.setHighlightColor(ColorConstants.yellow);
+ node.highlight();
+ }
+ else if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+ if ( connected.contains(bundle) ) {
+ node.setHighlightColor(ColorConstants.lightBlue);
+ node.highlight();
+ }
+ }
+ }
+ }
+ }
+
+ private void highlightLinks(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+ for ( Link l : graph.getLinks() ) {
+ GraphConnection c = (GraphConnection) findGraphItem(l);
+ if ( c != null ) {
+ c.unhighlight();
+
+ if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+ if ( l.getSource() == selected && connected.contains( l.getTarget() ) ) {
+ c.setHighlightColor(ColorConstants.lightBlue);
+ c.highlight();
+ }
+ }
+ }
+ }
+ }
+
+ private GraphItem findGraphItem(Object l) {
+ try {
+ return view.getGraphViewer().findGraphItem(l);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ // temporary fix for issue
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=242523
+ return null;
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
new file mode 100644
index 0000000..8fad30f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public class BundleGraph {
+
+ private HashMap<ISigilBundle, LinkedList<Link>> lookup = new HashMap<ISigilBundle, LinkedList<Link>>();
+ private LinkedList<Link> links = new LinkedList<Link>();
+ private HashSet<ISigilBundle> bundles = new HashSet<ISigilBundle>();
+
+ public void startResolution(IModelElement requirement) {
+ }
+
+ public void endResolution(IModelElement requirement, ISigilBundle target) {
+ ISigilBundle source = requirement.getAncestor(ISigilBundle.class);
+
+ bundles.add(source);
+ bundles.add(target);
+
+ LinkedList<Link> links = lookup.get(source);
+
+ if ( links == null ) {
+ links = new LinkedList<Link>();
+ lookup.put(source, links);
+ }
+
+ Link l = null;
+ for ( Link c : links ) {
+ if ( c.getTarget() == target ) {
+ l = c;
+ break;
+ }
+ }
+
+ if ( l == null ) {
+ l = new Link(source, target);
+ links.add(l);
+ this.links.add(l);
+ }
+
+ l.addRequirement(requirement);
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public Set<ISigilBundle> getBundles() {
+ return bundles;
+ }
+
+ public Set<ISigilBundle> getTargets(ISigilBundle bundle) {
+ HashSet<ISigilBundle> targets = new HashSet<ISigilBundle>();
+
+ for ( Link l : getLinks(bundle) ) {
+ targets.add(l.getTarget());
+ }
+
+ return targets;
+ }
+
+ public List<Link> getLinks(ISigilBundle selected) {
+ List<Link> l = lookup.get(selected);
+ return l == null ? Collections.<Link>emptyList() : l;
+ }
+
+ public List<Link> getDependentLinks(ISigilBundle bundle) {
+ ArrayList<Link> found = new ArrayList<Link>(links.size());
+
+ for (Link l : links) {
+ if ( l.getTarget() == bundle ) {
+ found.add( l );
+ }
+ }
+
+ found.trimToSize();
+
+ return found;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
new file mode 100644
index 0000000..0c0547c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.zest.core.viewers.IGraphContentProvider;
+
+public class BundleGraphContentProvider implements IGraphContentProvider {
+
+ public Object[] getElements(Object input) {
+ BundleGraph graph = (BundleGraph) input;
+ return graph.getLinks().toArray();
+ }
+
+ public Object getDestination(Object element) {
+ Link l = (Link) element;
+ return l.isSatisfied() ? l.getTarget() : new Link.Unsatisfied();
+ }
+
+ public Object getSource(Object element) {
+ Link l = (Link) element;
+ return l.getSource();
+ }
+
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
new file mode 100644
index 0000000..d48c253
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class BundleGraphLabelProvider extends LabelProvider {
+
+ private BundleResolverView view;
+
+ public BundleGraphLabelProvider(BundleResolverView view) {
+ this.view = view;
+ }
+
+ @Override
+ public String getText(Object element) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ if ( l.isSatisfied() ) {
+ if ( view.isDisplayed(BundleResolverView.LINK_LABELS) ) {
+ return view.getLinkText((Link) element);
+ }
+ else {
+ return "";
+ }
+ }
+ else {
+ return view.getLinkText((Link) element);
+ }
+ }
+ else if ( element instanceof ISigilBundle ) {
+ ISigilBundle b = (ISigilBundle) element;
+ return b.getBundleInfo().getSymbolicName() + ": " + b.getBundleInfo().getVersion();
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ return "unsatisfied";
+ }
+ else {
+ return "unknown:" + element;
+ }
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Image result = null;
+ if ( element instanceof ISigilBundle ) {
+ result = SigilUI.cacheImage("icons/jar_obj.png", BundleGraphLabelProvider.class.getClassLoader());
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ result = SigilUI.cacheImage("icons/error.gif", BundleGraphLabelProvider.class.getClassLoader());
+ }
+
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
new file mode 100644
index 0000000..4100ad8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class BundleGraphViewFilter extends ViewerFilter {
+
+ private BundleResolverView view;
+
+ public BundleGraphViewFilter(BundleResolverView view) {
+ this.view = view;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if ( !view.isDisplayed(BundleResolverView.LOCAL_LINKS) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return l.getSource() != l.getTarget();
+ }
+ }
+ if ( !view.isDisplayed(BundleResolverView.SATISFIED) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return !l.isSatisfied();
+ }
+ else if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ for ( Link l : view.getBundlegraph().getLinks(bundle)) {
+ if ( !l.isSatisfied() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ if ( !view.isDisplayed(BundleResolverView.UNSATISFIED) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return l.isSatisfied();
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
new file mode 100644
index 0000000..91c0dcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.zest.core.viewers.GraphViewer;
+import org.eclipse.zest.core.widgets.Graph;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.layouts.LayoutStyles;
+import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm;
+import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
+
+public class BundleResolverView extends ViewPart {
+
+ private static final String SHOW_LINK_LABELS = "Show link labels";
+ private static final String HIDE_LINK_LABELS = "Hide link labels";
+ private static final String SHOW_LOCAL_LINKS = "Show local links";
+ private static final String HIDE_LOCAL_LINKS = "Hide local links";
+ private static final String SHOW_DEPENDENTS = "Show dependents";
+ private static final String HIDE_DEPENDENTS = "Hide dependents";
+ private static final String SHOW_SATISFIED = "Show satisfied bundles";
+ private static final String HIDE_SATISFIED = "Hide satisfied bundles";
+ private static final String SHOW_UNSATISFIED = "Show unsatisfied bundles";
+ private static final String HIDE_UNSATISFIED = "Hide unsatisfied bundles";
+ private static final String SHOW_OPTIONAL = "Show optional dependencies";
+ private static final String HIDE_OPTIONAL = "Hide optional dependencies";
+
+ public static final String LINK_LABELS = "link.labels";
+ public static final String LOCAL_LINKS = "local.links";
+ public static final String DEPENDENTS = "dependents";
+ public static final String SATISFIED = "satisified";
+ public static final String UNSATISFIED = "unsatisfied";
+ public static final String OPTIONAL = "optional";
+
+ private GraphViewer viewer;
+ private IModelElement current;
+ private Job job;
+ private int lastX;
+ private int lastY;
+
+ private Map<String, Boolean> displayed = new HashMap<String, Boolean>();
+
+ private class ToggleDisplayAction extends Action {
+ private String key;
+ private String showMsg;
+ private String hideMsg;
+
+ ToggleDisplayAction(String key, String showMsg, String hideMsg) {
+ this.key = key;
+ this.showMsg = showMsg;
+ this.hideMsg = hideMsg;
+ setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+ }
+
+ @Override
+ public void run() {
+ BundleResolverView.this.setDisplayed( key, !BundleResolverView.this.isDisplayed(key) );
+ setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+ }
+ }
+
+ public void setInput(final IModelElement element) {
+ if ( current == null || !current.equals(element) ) {
+ SigilCore.log( "Set input " + element );
+ current = element;
+ redraw();
+ }
+ }
+
+ public void setDisplayed(String key, boolean b) {
+ displayed.put(key, b);
+
+ if ( key == DEPENDENTS ) {
+ int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+ viewer.setLayoutAlgorithm( b ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+ redraw();
+ }
+ else if ( key == OPTIONAL ) {
+ redraw();
+ }
+ else if ( key == SATISFIED || key == UNSATISFIED ) {
+ viewer.refresh();
+ }
+ }
+
+ public boolean isDisplayed(String key) {
+ return displayed.get(key);
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ @Override
+ public void createPartControl( Composite parent ) {
+ init();
+ createViewer(parent);
+ createListeners();
+ createMenu();
+ }
+
+ private void init() {
+ displayed.put(LINK_LABELS, false);
+ displayed.put(LOCAL_LINKS, false);
+ displayed.put(DEPENDENTS, false);
+ displayed.put(OPTIONAL, false);
+ displayed.put(SATISFIED, true);
+ displayed.put(UNSATISFIED, true);
+ }
+
+ public BundleGraph getBundlegraph() {
+ return (BundleGraph) viewer.getInput();
+ }
+
+ GraphViewer getGraphViewer() {
+ return viewer;
+ }
+
+ String getLinkText(Link link) {
+ StringBuffer buf = new StringBuffer();
+
+ for ( IModelElement e : link.getRequirements() ) {
+ if ( buf.length() > 0 ) {
+ buf.append( "\n" );
+ }
+ if ( e instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) e;
+ buf.append( "import " + pi.getPackageName() + " : " + pi.getVersions() + ": " + (pi.isOptional() ? "optional" : "mandatory" ) );
+ }
+ else if ( e instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) e;
+ buf.append( "required bundle " + rb.getSymbolicName() + " : " + rb.getVersions() + ": " + (rb.isOptional() ? "optional" : "mandatory" ) );
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private synchronized void redraw() {
+ final IModelElement element = current;
+ if ( job != null ) {
+ job.cancel();
+ }
+
+ job = new Job("Resolving " + current) {
+ @Override
+ protected IStatus run(IProgressMonitor progress) {
+ try {
+ resolve(element, progress);
+ return Status.OK_STATUS;
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ }
+ };
+ job.schedule();
+ }
+
+ private void resolve(IModelElement element, IProgressMonitor progress) throws CoreException {
+ final BundleGraph graph = new BundleGraph();
+
+ IResolutionMonitor monitor = new ResolutionMonitorAdapter(progress) {
+ @Override
+ public void startResolution(IModelElement requirement) {
+ graph.startResolution(requirement);
+ }
+
+ @Override
+ public void endResolution(IModelElement requirement, ISigilBundle provider) {
+ graph.endResolution(requirement, provider);
+ }
+ };
+
+ ISigilProjectModel project = findProject(element);
+ IRepositoryManager repository = SigilCore.getRepositoryManager(project);
+
+ int options = ResolutionConfig.IGNORE_ERRORS;
+
+ if ( isDisplayed(DEPENDENTS) ) {
+ options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+ }
+ if ( isDisplayed(OPTIONAL) ) {
+ options |= ResolutionConfig.INCLUDE_OPTIONAL;
+ }
+
+ ResolutionConfig config = new ResolutionConfig(options);
+
+ try {
+ repository.getBundleResolver().resolve(element, config, monitor);
+ } catch (ResolutionException e) {
+ throw SigilCore.newCoreException("Failed to resolve " + element, e);
+ }
+
+ SigilUI.runInUI( new Runnable() {
+ public void run() {
+ viewer.setInput(graph);
+ addCustomUIElements();
+ }
+ } );
+ }
+
+ private static ISigilProjectModel findProject(IModelElement element) {
+ if ( element == null ) {
+ return null;
+ }
+ if ( element instanceof ISigilProjectModel ) {
+ return (ISigilProjectModel) element;
+ }
+
+ return element.getAncestor(ISigilProjectModel.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void addCustomUIElements() {
+ if ( !isDisplayed(LINK_LABELS) ) {
+ for ( GraphConnection c : (List<GraphConnection>) viewer.getGraphControl().getConnections() ) {
+ if ( c.getData() instanceof Link ) {
+ c.setTooltip(buildToolTip((Link) c.getData()));
+ }
+ }
+ }
+ }
+
+ private IFigure buildToolTip(Link link) {
+ Label l = new Label();
+ l.setText(getLinkText(link));
+ return l;
+ }
+
+ private void createViewer(Composite parent) {
+ parent.setLayout( new FillLayout() );
+ viewer = new GraphViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+ IContentProvider contentProvider = new BundleGraphContentProvider();
+ viewer.setContentProvider(contentProvider);
+ viewer.setLabelProvider(new BundleGraphLabelProvider(this));
+ viewer.addFilter( new BundleGraphViewFilter(this) );
+
+
+ int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+ viewer.setLayoutAlgorithm( isDisplayed(DEPENDENTS) ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+ viewer.addSelectionChangedListener( new BundleConnectionHighlighter(this) );
+ viewer.setInput(new BundleGraph());
+ }
+
+ private void createMenu() {
+ IActionBars action = getViewSite().getActionBars();
+ action.getMenuManager().add(new ToggleDisplayAction( LINK_LABELS, SHOW_LINK_LABELS, HIDE_LINK_LABELS ));
+ action.getMenuManager().add(new ToggleDisplayAction( LOCAL_LINKS, SHOW_LOCAL_LINKS, HIDE_LOCAL_LINKS ));
+ action.getMenuManager().add(new ToggleDisplayAction( DEPENDENTS, SHOW_DEPENDENTS, HIDE_DEPENDENTS ));
+ action.getMenuManager().add(new ToggleDisplayAction( OPTIONAL, SHOW_OPTIONAL, HIDE_OPTIONAL ));
+ action.getMenuManager().add(new ToggleDisplayAction( SATISFIED, SHOW_SATISFIED, HIDE_SATISFIED ));
+ action.getMenuManager().add(new ToggleDisplayAction( UNSATISFIED, SHOW_UNSATISFIED, HIDE_UNSATISFIED ));
+ action.updateActionBars();
+ }
+
+ private void createListeners() {
+ IPartService ps = (IPartService) getViewSite().getService(IPartService.class);
+ ps.addPartListener( new EditorViewPartListener(this) );
+ viewer.getGraphControl().addControlListener( new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Graph g = (Graph) e.getSource();
+ int x = g.getSize().x;
+ int y = g.getSize().y;
+ if ( lastX != x || lastY != y ) {
+ lastX = x;
+ lastY = y;
+ redraw();
+ }
+ }
+ } );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
new file mode 100644
index 0000000..78bceb7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPartReference;
+
+public class EditorViewPartListener implements IPartListener2 {
+
+ private BundleResolverView bundleResolverView;
+
+ public EditorViewPartListener(BundleResolverView bundleResolverView) {
+ this.bundleResolverView = bundleResolverView;
+ }
+
+ public void partActivated(IWorkbenchPartReference partRef) {
+ checkRef(partRef);
+ }
+
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partClosed(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partHidden(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partOpened(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partVisible(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ private void checkRef(IWorkbenchPartReference partRef) {
+ IEditorPart editor = partRef.getPage().getActiveEditor();
+ if ( editor != null ) {
+ IEditorInput input = editor.getEditorInput();
+ if ( input instanceof IFileEditorInput ) {
+ IFileEditorInput f = (IFileEditorInput) input;
+ IProject project = f.getFile().getProject();
+ try {
+ ISigilProjectModel model = SigilCore.create(project);
+ if ( model != null ) {
+ bundleResolverView.setInput(model);
+ }
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
new file mode 100644
index 0000000..35591a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class Link {
+ public static class Unsatisfied {
+
+ }
+
+ private ISigilBundle source;
+ private ISigilBundle target;
+
+ private LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();
+ private static final Comparator<IModelElement> comparator = new Comparator<IModelElement>() {
+
+ public int compare(IModelElement o1, IModelElement o2) {
+ if (o1 instanceof IRequiredBundle) {
+ if ( o2 instanceof IRequiredBundle) {
+ return compareBundles( (IRequiredBundle) o1, (IRequiredBundle) o2 );
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ if ( o2 instanceof IRequiredBundle ) {
+ return 1;
+ }
+ else {
+ return compareNonBundles( o1, o2 );
+ }
+ }
+ }
+
+ private int compareNonBundles(IModelElement o1, IModelElement o2) {
+ if (o1 instanceof IPackageImport) {
+ if ( o2 instanceof IPackageImport) {
+ return compareImports( (IPackageImport) o1, (IPackageImport) o2 );
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ if ( o2 instanceof IPackageImport ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ }
+
+ private int compareImports(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo( o2.getPackageName() );
+ }
+
+ private int compareBundles(IRequiredBundle o1, IRequiredBundle o2) {
+ return o1.getSymbolicName().compareTo( o2.getSymbolicName() );
+ }
+
+ };
+
+ public Link(ISigilBundle source, ISigilBundle target) {
+ this.source = source;
+ this.target = target;
+ }
+
+ public ISigilBundle getSource() {
+ return source;
+ }
+
+ public ISigilBundle getTarget() {
+ return target;
+ }
+
+ public boolean isSatisfied() {
+ return target != null;
+ }
+
+ public void addRequirement(IModelElement requirement) {
+ requirements.add(requirement);
+ Collections.sort(requirements, comparator);
+ }
+
+ public String toString() {
+ return "Link[" + source + "->" + target + "]";
+ }
+
+ public List<IModelElement> getRequirements() {
+ return requirements;
+ }
+
+ public boolean isOptional() {
+ for ( IModelElement e : requirements ) {
+ if ( e instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) e;
+ if ( !pi.isOptional() ) {
+ return false;
+ }
+ }
+ else if ( e instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) e;
+ if ( !rb.isOptional() ) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
new file mode 100644
index 0000000..bc4a39d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public abstract class SigilNewResourceWizard extends BasicNewResourceWizard implements INewWizard {
+
+ protected void selectRevealAndShow(IFile file) {
+ selectAndReveal(file);
+
+ // Open editor on new file.
+ IWorkbenchWindow dw = getWorkbench().getActiveWorkbenchWindow();
+ try {
+ if (dw != null) {
+ IWorkbenchPage page = dw.getActivePage();
+ if (page != null) {
+ IDE.openEditor(page, file, true);
+ }
+ }
+ }
+ catch (PartInitException e) {
+ MessageDialog.openError(Display.getCurrent().getActiveShell(), "Initialisation error",
+ "Failed to open " + file);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
new file mode 100644
index 0000000..cbab33b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class WorkspaceContentProvider implements ITreeContentProvider {
+
+ private final boolean includeNonSigil;
+ private final boolean includeClosed;
+
+ public WorkspaceContentProvider(boolean includeNonSigil, boolean includeClosed) {
+ this.includeNonSigil = includeNonSigil;
+ this.includeClosed = includeClosed;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ Object[] result = null;
+
+ if(parentElement instanceof IWorkspace) {
+ IProject[] projects = ((IWorkspace) parentElement).getRoot().getProjects();
+ if(includeNonSigil && includeClosed) {
+ result = projects;
+ } else {
+ List<IProject> includedProjects = new ArrayList<IProject>(projects.length);
+ for (IProject project : projects) {
+ if(!includeClosed && !project.isOpen()) {
+ continue;
+ }
+
+ if(!includeNonSigil) {
+ try {
+ if(project.getNature(SigilCore.NATURE_ID) == null) {
+ continue;
+ }
+ } catch (CoreException e) {
+ continue;
+ }
+ }
+
+ includedProjects.add(project);
+ }
+ result = includedProjects.toArray(new IProject[includedProjects.size()]);
+ }
+ } else if(parentElement instanceof IContainer) {
+ try {
+ IResource[] members = ((IContainer) parentElement).members();
+ List<IResource> children = new ArrayList<IResource>(members.length);
+ for (int i = 0; i < members.length; i++) {
+ if (members[i].getType() != IResource.FILE) {
+ children.add(members[i]);
+ }
+ }
+ result = children.toArray(new IResource[children.size()]);
+ } catch (CoreException e) {
+ // Shouldn't happen
+ }
+ }
+
+ return result;
+ }
+
+ public Object getParent(Object element) {
+ if(element instanceof IResource) {
+ return ((IResource) element).getParent();
+ }
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ return (element instanceof IContainer) && ((IContainer) element).isAccessible();
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return getChildren(inputElement);
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
new file mode 100644
index 0000000..8f926bf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.wizard.SigilNewResourceWizard;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizard extends SigilNewResourceWizard implements IExecutableExtension {
+
+ private SigilProjectWizardFirstPage firstPage;
+ private SigilProjectWizardSecondPage secondPage;
+
+ private String name;
+
+ public static final IPath SIGIL_PROJECT_PATH = new Path( SigilCore.SIGIL_PROJECT_FILE );
+ private IConfigurationElement config;
+
+ public void init(IWorkbench workbench, IStructuredSelection currentSelection) {
+ super.init(workbench, currentSelection);
+
+ firstPage = new SigilProjectWizardFirstPage();
+ firstPage.setInitialProjectName(name);
+ secondPage = new SigilProjectWizardSecondPage(firstPage);
+
+ addPage(firstPage);
+ addPage(secondPage);
+ }
+
+ private void finishPage(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ secondPage.performFinish(monitor);
+
+ IProject newProject = firstPage.getProjectHandle();
+
+ if ( newProject != null && newProject.exists() ) {
+ IFile file = newProject.getFile( SigilProjectWizard.SIGIL_PROJECT_PATH );
+
+ selectRevealAndShow( file );
+
+ new Job("Check OSGi Install" ) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // prompt for osgi home if not already set.
+ SigilCore.getInstallManager().getDefaultInstall();
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ try {
+ finishPage(monitor);
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException(e.getMessage());
+ }
+ }
+ };
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to complete project wizard", e);
+ return false;
+ }
+
+ BasicNewProjectResourceWizard.updatePerspective(config);
+ return true;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean performCancel() {
+ secondPage.performCancel();
+ return super.performCancel();
+ }
+
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data)
+ throws CoreException {
+ this.config = config;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
new file mode 100644
index 0000000..78eaf19
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardFirstPage extends WizardNewProjectCreationPage {
+
+ private volatile String description = "";
+ private volatile Version version = new Version(1, 0, 0);
+ private volatile String vendor = "";
+ private volatile String name = "";
+
+ private Text txtDescription;
+ private Text txtVersion;
+ private Text txtVendor;
+ private Text txtName;
+
+ public SigilProjectWizardFirstPage() {
+ super("newSigilProjectPage");
+ setTitle( "Sigil Project" );
+ setDescription( "Create a new Sigil project" );
+ }
+
+ public boolean isInWorkspace() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IPath defaultDefaultLocation = workspace.getRoot().getLocation();
+
+ return defaultDefaultLocation.isPrefixOf( getLocationPath() );
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ boolean result = super.isPageComplete();
+ return result;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ FieldDecoration infoDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
+
+ // Create controls
+ super.createControl(parent);
+ Composite control = (Composite) getControl();
+
+ Group grpProjectSettings = new Group(control, SWT.NONE);
+ grpProjectSettings.setText("Project Settings");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Version:");
+ txtVersion = new Text(grpProjectSettings, SWT.BORDER);
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Name:");
+ txtName = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtNameDecor = new ControlDecoration(txtName, SWT.LEFT | SWT.CENTER);
+ txtNameDecor.setImage(infoDecor.getImage());
+ txtNameDecor.setDescriptionText("Defines a human-readable name for the bundle");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Description:");
+ txtDescription = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtDescDecor = new ControlDecoration(txtDescription, SWT.LEFT | SWT.CENTER);
+ txtDescDecor.setImage(infoDecor.getImage());
+ txtDescDecor.setDescriptionText("Defines a short human-readable description for the bundle");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Provider:");
+ txtVendor = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtVendorDecor = new ControlDecoration(txtVendor, SWT.LEFT | SWT.CENTER);
+ txtVendorDecor.setImage(infoDecor.getImage());
+ txtVendorDecor.setDescriptionText("The name of the company, organisation or individual providing the bundle");
+
+ // Set values
+ txtDescription.setText(description);
+ txtVersion.setText(version.toString());
+ txtVendor.setText(vendor);
+ txtName.setText(name);
+
+ // Add listeners
+ ModifyListener txtModListener = new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ description = txtDescription.getText();
+ vendor = txtVendor.getText();
+ name = txtName.getText();
+
+ validateSettings();
+ }
+ };
+ txtDescription.addModifyListener(txtModListener);
+ txtVersion.addModifyListener(txtModListener);
+ txtVendor.addModifyListener(txtModListener);
+ txtName.addModifyListener(txtModListener);
+
+ // Layout
+ control.setLayout(new GridLayout());
+ grpProjectSettings.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ grpProjectSettings.setLayout(new GridLayout(2, false));
+ txtDescription.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVendor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ }
+
+ private void validateSettings() {
+ try {
+ version = new Version(txtVersion.getText());
+ } catch (IllegalArgumentException e) {
+ version = null;
+ setErrorMessage("Invalid version");
+ setPageComplete(false);
+ return;
+ }
+
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
new file mode 100644
index 0000000..3b2592d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.PreferenceConstants;
+import org.eclipse.jdt.ui.wizards.JavaCapabilityConfigurationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardSecondPage extends JavaCapabilityConfigurationPage {
+
+ private SigilProjectWizardFirstPage firstPage;
+ private IProject currentProject;
+ private URI currentProjectLocation;
+ private boolean created;
+
+ public SigilProjectWizardSecondPage(SigilProjectWizardFirstPage firstPage) {
+ this.firstPage = firstPage;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ changeToNewProject();
+ } else {
+ removeProject();
+ }
+ }
+
+ @Override
+ protected boolean useNewSourcePage() {
+ return true;
+ }
+
+ protected void performFinish(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ changeToNewProject();
+ updateProject(monitor);
+ }
+
+ private void changeToNewProject() {
+ if ( !created ) {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ try {
+ updateProject(monitor);
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException(e.getMessage());
+ }
+ }
+ };
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ setErrorMessage(null);
+ setPageComplete(true);
+ created = true;
+ }
+ catch (CoreException e) {
+ SigilCore.error("Failed to run workspace job", e);
+ }
+ }
+ }
+
+ private void removeProject() {
+ if (currentProject == null || !currentProject.exists()) {
+ return;
+ }
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ doRemoveProject(monitor);
+ }
+ };
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ }
+ catch (CoreException e) {
+ SigilCore.error("Failed to run workspace job", e);
+ }
+ finally {
+ created = false;
+ }
+ }
+
+ private void updateProject(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ currentProject = firstPage.getProjectHandle();
+ currentProjectLocation= getProjectLocationURI();
+
+ String description = firstPage.getDescription();
+ Version projectVersion = firstPage.getVersion();
+ String vendor = firstPage.getVendor();
+ String name = firstPage.getName();
+
+ createProject( currentProject, currentProjectLocation, monitor);
+
+ IPath src = createSourcePath();
+
+ IPath output = getOutputLocation();
+
+ if ( output.segmentCount() == 0 ) {
+ output = new Path( currentProject.getName() ).append( "build" ).append( "classes" );
+ }
+
+ IClasspathEntry[] entries = getProjectClassPath(src);
+
+ SigilCore.makeSigilProject(currentProject, monitor);
+
+ init(JavaCore.create(currentProject), output.makeRelative(), entries, false);
+
+ configureJavaProject(new SubProgressMonitor(monitor, 3));
+
+ configureSigilProject( currentProject, description, projectVersion, vendor, name, src, monitor );
+ }
+
+ private IPath createSourcePath() throws CoreException {
+ IPath projectPath = currentProject.getFullPath();
+ IPath src = new Path( "src" );
+ IFolder f = currentProject.getFolder( src );
+ if ( !f.getLocation().toFile().exists() ) {
+ f.create(true, true, null);
+ }
+
+ return projectPath.append(src);
+ }
+
+ final void doRemoveProject(IProgressMonitor monitor) throws CoreException {
+ final boolean noProgressMonitor= (currentProjectLocation == null); // inside workspace
+
+ if (monitor == null || noProgressMonitor) {
+ monitor= new NullProgressMonitor();
+ }
+ monitor.beginTask("Remove project", 3);
+ try {
+ try {
+ boolean removeContent= currentProject.isSynchronized(IResource.DEPTH_INFINITE);
+ currentProject.delete(removeContent, false, new SubProgressMonitor(monitor, 2));
+
+ } finally {
+ }
+ } finally {
+ monitor.done();
+ currentProject= null;
+ }
+ }
+
+ private IClasspathEntry[] getProjectClassPath(IPath src) throws CoreException {
+ List<IClasspathEntry> cpEntries= new ArrayList<IClasspathEntry>();
+ cpEntries.add(JavaCore.newSourceEntry(src));
+ cpEntries.addAll(Arrays.asList(getDefaultClasspathEntry()));
+ cpEntries.add(JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH)));
+ IClasspathEntry[] entries= cpEntries.toArray(new IClasspathEntry[cpEntries.size()]);
+
+ return entries;
+ }
+
+ private IClasspathEntry[] getDefaultClasspathEntry() {
+ IClasspathEntry[] defaultJRELibrary= PreferenceConstants.getDefaultJRELibrary();
+ /*String compliance= firstPage.getCompilerCompliance();
+ IPath jreContainerPath= new Path(JavaRuntime.JRE_CONTAINER);
+ if (compliance == null || defaultJRELibrary.length > 1 || !jreContainerPath.isPrefixOf(defaultJRELibrary[0].getPath())) {
+ // use default
+ return defaultJRELibrary;
+ }
+ IVMInstall inst= firstPage.getJVM();
+ if (inst != null) {
+ IPath newPath= jreContainerPath.append(inst.getVMInstallType().getId()).append(inst.getName());
+ return new IClasspathEntry[] { JavaCore.newContainerEntry(newPath) };
+ }*/
+ return defaultJRELibrary;
+ }
+
+
+ private void configureSigilProject( IProject project, String description, Version projectVersion, String vendorName, String bundleName, IPath src, IProgressMonitor monitor ) throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project);
+ IClasspathEntry cp = JavaCore.newSourceEntry(src);
+ String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry(cp );
+
+ ISigilBundle bundle = sigil.getBundle();
+ bundle.addClasspathEntry(encodedClasspath);
+
+ if(description != null) {
+ bundle.getBundleInfo().setDescription(description);
+ }
+ if(projectVersion != null) {
+ bundle.setVersion(projectVersion);
+ }
+ if(vendorName != null) {
+ bundle.getBundleInfo().setVendor(vendorName);
+ }
+ if(bundleName != null) {
+ bundle.getBundleInfo().setName(bundleName);
+ }
+ sigil.save(monitor);
+ }
+
+
+ private URI getProjectLocationURI() throws CoreException {
+ if (firstPage.isInWorkspace()) {
+ return null;
+ }
+ return firstPage.getLocationURI();
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ boolean result = super.isPageComplete();
+ return result;
+ }
+
+ protected void performCancel() {
+ if(currentProject != null) {
+ removeProject();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
new file mode 100644
index 0000000..c2403e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+
+public class RepositoryWizard extends Wizard {
+
+ private IRepositoryModel model;
+
+ @Override
+ public boolean performFinish() {
+ for ( IWizardPage page : getPages() ) {
+ if ( page instanceof RepositoryWizardPage ) {
+ RepositoryWizardPage rwp = (RepositoryWizardPage) page;
+ rwp.storeFields();
+ }
+ }
+ return true;
+ }
+
+ public IRepositoryModel getModel() {
+ return model;
+ }
+
+ public void init(IRepositoryModel model) {
+ this.model = model;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
new file mode 100644
index 0000000..9a22aa4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class RepositoryWizardPage extends WizardPage {
+
+ private StringFieldEditor nameEditor;
+ private ArrayList<FieldEditor> editors = new ArrayList<FieldEditor>();
+ private RepositoryWizard wizard;
+
+ protected RepositoryWizardPage(String pageName, RepositoryWizard parent) {
+ super(pageName);
+ setTitle(pageName);
+ this.wizard = parent;
+ }
+
+ public abstract void createFieldEditors();
+
+ public void addField(FieldEditor editor) {
+ editors.add( editor );
+ }
+
+ public void createControl(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+ setControl(control);
+
+ if ( getModel().getType().isDynamic() ) {
+ nameEditor = new StringFieldEditor("name", "Name:", control);
+ nameEditor.setStringValue(getModel().getName());
+ nameEditor.getTextControl(getFieldEditorParent()).addModifyListener( new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ checkPageComplete();
+ }
+ });
+ }
+
+ createFieldEditors();
+
+ int cols = nameEditor == null ? 0 : nameEditor.getNumberOfControls();
+ for ( FieldEditor e : editors ) {
+ cols = Math.max(cols, e.getNumberOfControls());
+ }
+
+ control.setLayout( new GridLayout(cols, false) );
+
+ if ( nameEditor != null ) {
+ nameEditor.fillIntoGrid(getFieldEditorParent(), cols);
+ }
+
+ for ( FieldEditor e : editors ) {
+ e.fillIntoGrid(getFieldEditorParent(), cols);
+ e.setPreferenceStore(getModel().getPreferences());
+ e.load();
+ }
+
+ checkPageComplete();
+ }
+
+ protected void checkPageComplete() {
+ if ( nameEditor != null ) {
+ setPageComplete(nameEditor.getStringValue().length() > 0);
+ }
+ }
+
+ public IRepositoryModel getModel() {
+ return wizard.getModel();
+ }
+
+ protected Composite getFieldEditorParent() {
+ return (Composite) getControl();
+ }
+
+ public void storeFields() {
+ getModel().setName(nameEditor.getStringValue());
+ for ( FieldEditor e : editors ) {
+ e.store();
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.updatesite/.project b/sigil/org.cauldron.sigil.updatesite/.project
new file mode 100644
index 0000000..b538dcd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.updatesite/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.updatesite</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.UpdateSiteBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.UpdateSiteNature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.updatesite/site.xml b/sigil/org.cauldron.sigil.updatesite/site.xml
new file mode 100644
index 0000000..6b32f0a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.updatesite/site.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<site>
+ <feature url="features/org.cauldron.sigil.core_0.3.0.jar" id="org.cauldron.sigil.core" version="0.3.0">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.1.jar" id="org.cauldron.sigil.core" version="0.3.1">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.2.jar" id="org.cauldron.sigil.core" version="0.3.2">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.3.jar" id="org.cauldron.sigil.core" version="0.3.3">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.4.jar" id="org.cauldron.sigil.core" version="0.3.4">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.5.jar" id="org.cauldron.sigil.core" version="0.3.5">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.6.jar" id="org.cauldron.sigil.core" version="0.3.6">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.7.jar" id="org.cauldron.sigil.core" version="0.3.7">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.8.jar" id="org.cauldron.sigil.core" version="0.3.8">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.9.jar" id="org.cauldron.sigil.core" version="0.3.9">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.10.jar" id="org.cauldron.sigil.core" version="0.3.10">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.11.jar" id="org.cauldron.sigil.core" version="0.3.11">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.12.jar" id="org.cauldron.sigil.core" version="0.3.12">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.13.jar" id="org.cauldron.sigil.core" version="0.3.13">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.14.jar" id="org.cauldron.sigil.core" version="0.3.14">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.15.jar" id="org.cauldron.sigil.core" version="0.3.15">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.16.jar" id="org.cauldron.sigil.core" version="0.3.16">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.17.jar" id="org.cauldron.sigil.core" version="0.3.17">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.3.18.jar" id="org.cauldron.sigil.core" version="0.3.18">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.0.jar" id="org.cauldron.sigil.core" version="0.4.0">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.1.jar" id="org.cauldron.sigil.core" version="0.4.1">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.2.jar" id="org.cauldron.sigil.core" version="0.4.2">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.3.jar" id="org.cauldron.sigil.core" version="0.4.3">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.4.jar" id="org.cauldron.sigil.core" version="0.4.4">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.5.jar" id="org.cauldron.sigil.core" version="0.4.5">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.6.jar" id="org.cauldron.sigil.core" version="0.4.6">
+ <category name="Sigil"/>
+ </feature>
+ <feature url="features/org.cauldron.sigil.core_0.4.7.jar" id="org.cauldron.sigil.core" version="0.4.7">
+ <category name="Sigil"/>
+ </feature>
+ <category-def name="Sigil" label="Sigil">
+ <description>
+ Sigil is an SDK for developing applications to be deployed on the Newton framework (http://newton.codecauldron.org)
+ </description>
+ </category-def>
+</site>
diff --git a/sigil/org.cauldron.sigil.utils/.classpath b/sigil/org.cauldron.sigil.utils/.classpath
new file mode 100644
index 0000000..64c5e31
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.utils/.project b/sigil/org.cauldron.sigil.utils/.project
new file mode 100644
index 0000000..0e1390a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.utils</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs b/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..0710b5a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Tue Aug 12 11:45:38 BST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..7df73f4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sigil Utils Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.utils;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Require-Bundle: org.eclipse.core.runtime
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: org.eclipse.core.expressions,
+ org.eclipse.core.resources,
+ org.eclipse.ui
+Export-Package: org.cauldron.sigil.utils
diff --git a/sigil/org.cauldron.sigil.utils/build.properties b/sigil/org.cauldron.sigil.utils/build.properties
new file mode 100644
index 0000000..e9863e2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
diff --git a/sigil/org.cauldron.sigil.utils/plugin.xml b/sigil/org.cauldron.sigil.utils/plugin.xml
new file mode 100644
index 0000000..0d14f33
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/plugin.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="org.cauldron.sigil.utils.properties.EditorPropertyTester"
+ id="org.cauldron.sigil.utils.editorPropertyTester"
+ namespace="org.cauldron.sigil"
+ properties="isEditorOfType"
+ type="org.eclipse.ui.IWorkbenchPart">
+ </propertyTester>
+ <propertyTester
+ class="org.cauldron.sigil.utils.properties.PartKindPropertyTester"
+ id="org.cauldron.sigil.runtime.PartKindPropertyTester"
+ namespace="org.cauldron.sigil"
+ properties="partKind,partId"
+ type="org.eclipse.ui.IWorkbenchPart">
+ </propertyTester>
+ <propertyTester
+ class="org.cauldron.sigil.utils.properties.ResourceTypePropertyTester"
+ id="org.cauldron.sigil.utils.ResourcePropertyTester"
+ namespace="org.cauldron.sigil"
+ properties="isResourceOfType"
+ type="org.eclipse.core.resources.IResource">
+ </propertyTester>
+ </extension>
+
+</plugin>
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java
new file mode 100644
index 0000000..401c390
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/GlobCompiler.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import java.util.regex.Pattern;
+
+public class GlobCompiler {
+ public static final Pattern compile(String glob) {
+ char[] chars = glob.toCharArray();
+ if ( chars.length > 0 ) {
+ StringBuilder builder = new StringBuilder(chars.length + 5);
+
+ builder.append('^');
+
+ for (char c : chars) {
+ switch ( c ) {
+ case '*':
+ builder.append(".*");
+ break;
+ case '.':
+ builder.append("\\.");
+ break;
+ case '$':
+ builder.append( "\\$" );
+ break;
+ default:
+ builder.append( c );
+ }
+ }
+
+ return Pattern.compile(builder.toString());
+ }
+ else {
+ return Pattern.compile(glob);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java
new file mode 100644
index 0000000..00de2b9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/PathHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import java.io.File;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class PathHelper {
+
+ public static void scanFiles(List<IPath> paths, IPath path, String pattern, boolean recurse) {
+ Pattern p = GlobCompiler.compile(pattern);
+
+ for ( File f : path.toFile().listFiles() ) {
+ if ( f.isDirectory() && recurse ) {
+ scanFiles( paths, new Path(f.getAbsolutePath()), pattern, recurse);
+ }
+ else if ( f.isFile() && p.matcher(f.getName()).matches() ) {
+ paths.add( new Path(f.getAbsolutePath()) );
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java
new file mode 100644
index 0000000..6f379d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/SigilUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+
+public final class SigilUtils {
+ private SigilUtils () {
+ }
+
+ public static boolean isResourceType(IResource resource, String type) {
+ IContentType[] types = Platform.getContentTypeManager().findContentTypesFor(resource.getName());
+ for (IContentType contentType : types) {
+ if(contentType.getId().equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java
new file mode 100644
index 0000000..ce6ad86
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/EditorPropertyTester.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.cauldron.sigil.utils.SigilUtils;
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class EditorPropertyTester extends PropertyTester {
+
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ IWorkbenchPart part = (IWorkbenchPart) receiver;
+
+ boolean result = false;
+
+ if(part instanceof IEditorPart) {
+ IEditorInput input = ((IEditorPart) part).getEditorInput();
+ if(input instanceof IFileEditorInput) {
+ IFile file = ((IFileEditorInput) input).getFile();
+
+ if("isEditorOfType".equals(property)) {
+ result = SigilUtils.isResourceType(file, (String) expectedValue);
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java
new file mode 100644
index 0000000..b0dceee
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/PartKindPropertyTester.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class PartKindPropertyTester extends PropertyTester{
+
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ IWorkbenchPart part = (IWorkbenchPart) receiver;
+
+ Object value;
+ if("partKind".equals(property)) {
+ if(part instanceof IEditorPart) {
+ value = "editor";
+ } else if(part instanceof IViewPart) {
+ value = "view";
+ } else {
+ value = null;
+ }
+ } else if("partId".equals(property)) {
+ value = part.getSite().getId();
+ } else {
+ value = null;
+ }
+
+ return expectedValue.equals(value);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java
new file mode 100644
index 0000000..efc32e3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.utils/src/org/cauldron/sigil/utils/properties/ResourceTypePropertyTester.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.utils.properties;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+
+public class ResourceTypePropertyTester extends PropertyTester {
+
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if (!(receiver instanceof IResource)) {
+ return false;
+ }
+
+ boolean result = false;
+
+ IResource resource = (IResource) receiver;
+ if ("isResourceOfType".equals(property)) {
+ IContentType[] types = Platform.getContentTypeManager().findContentTypesFor(
+ resource.getName());
+
+ for (IContentType type : types) {
+ if (type.getId().equals(expectedValue)) {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/sigil/sigil-builder/.classpath b/sigil/sigil-builder/.classpath
new file mode 100644
index 0000000..5805c94
--- /dev/null
+++ b/sigil/sigil-builder/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="build-libs/classes" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="build-libs/ant.jar"/>
+ <classpathentry kind="lib" path="build-libs/osgi.core.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/sigil-builder/.project b/sigil/sigil-builder/.project
new file mode 100644
index 0000000..646a5cd
--- /dev/null
+++ b/sigil/sigil-builder/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>sigil-builder</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/sigil-builder/build.xml b/sigil/sigil-builder/build.xml
new file mode 100644
index 0000000..4161e54
--- /dev/null
+++ b/sigil/sigil-builder/build.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="sigil-builder" default="build">
+ <property name="user.props" value="user.properties" />
+ <property file="${user.props}"/>
+ <property file="project.properties"/>
+ <property file="download.properties"/>
+
+ <property name="error.internal.target" value="Do not call this target directly"/>
+
+ <property name="eclipse.sdk" value="${eclipse.install.dir}"/>
+ <property name="extras.sdk" value="build-libs/target-platform-extras"/>
+
+ <condition property="isUnix">
+ <os family="unix"/>
+ </condition>
+
+ <!-- PUBLIC TARGETS -->
+
+ <target name="build"
+ depends="clean-tmp, target-platform, pdebuild"/>
+
+ <target name="clean-tmp">
+ <delete dir="target/tmp" />
+ </target>
+
+ <target name="clean">
+ <delete dir="target"/>
+ <delete dir="build-libs/classes"/>
+ </target>
+
+ <!-- create target platform -->
+ <target name="target-platform" depends="rsync-platform, copy-platform"/>
+
+ <target name="rsync-platform" if="isUnix">
+ <mkdir dir="${target.platform}/eclipse" />
+ <exec executable="rsync" append="true"
+ output="${target.platform}/rsync.log" >
+ <arg line="-av --delete ${eclipse.sdk}/ ${extras.sdk}/ ${target.platform}/eclipse" />
+ </exec>
+ </target>
+
+ <target name="copy-platform" unless="isUnix">
+ <mkdir dir="${target.platform}/eclipse" />
+ <copy todir="${target.platform}/eclipse">
+ <fileset dir="${eclipse.sdk}"/>
+ <fileset dir="${extras.sdk}" />
+ </copy>
+ </target>
+
+ <target name="init">
+ <mkdir dir="target"/>
+ <mkdir dir="target/features"/>
+ <mkdir dir="target/plugins"/>
+ </target>
+
+ <target name="custom.tasks">
+ <mkdir dir="build-libs/classes"/>
+ <javac srcdir="src" destdir="build-libs/classes" debug="on">
+ <classpath>
+ <fileset dir="build-libs">
+ <include name="osgi.core.jar"/>
+ </fileset>
+ </classpath>
+ </javac>
+ <taskdef name="findbundle" classname="org.cauldron.sigil.build.FindBundlesTask">
+ <classpath>
+ <pathelement location="build-libs/osgi.core.jar"/>
+ <pathelement location="build-libs/classes"/>
+ </classpath>
+ </taskdef>
+ <taskdef name="siteInsertFeatures" classname="org.cauldron.sigil.build.SiteInsertFeatures">
+ <classpath>
+ <pathelement location="build-libs/classes"/>
+ </classpath>
+ </taskdef>
+ </target>
+
+ <target name="findbundles" depends="custom.tasks">
+ <!-- Find the Equinox launcher JAR -->
+ <echo>${eclipse.install.dir}</echo>
+ <findbundle dir="${eclipse.install.dir}/plugins" symbolicname="org.eclipse.equinox.launcher" property="eclipse.launcher.version"/>
+ <property name="eclipse.launcher.jar" location="${eclipse.install.dir}/plugins/org.eclipse.equinox.launcher_${eclipse.launcher.version}.jar"/>
+
+ <!-- Find the PDE Build scripts -->
+ <findbundle dir="${eclipse.install.dir}/plugins" symbolicname="org.eclipse.pde.build" property="eclipse.pdebuild.version"/>
+ <property name="eclipse.productBuild.xml" location="${eclipse.install.dir}/plugins/org.eclipse.pde.build_${eclipse.pdebuild.version}/scripts/productBuild/productBuild.xml"/>
+ <property name="eclipse.featureBuild.xml" location="${eclipse.install.dir}/plugins/org.eclipse.pde.build_${eclipse.pdebuild.version}/scripts/build.xml"/>
+ </target>
+
+ <!-- Unused
+ <target name="download" depends="init">
+ <mkdir dir="${target.platform}"/>
+
+ <echo message="Downloading platform runtime"/>
+ <get dest="${target.platform}/platform-runtime.zip"
+ src="${download.base}/${download.dir}/${download.platform.runtime}" verbose="true"/>
+
+ <echo message="Downloading RCP delta pack"/>
+ <get dest="${target.platform}/deltapack.zip"
+ src="${download.base}/${download.dir}/${download.deltapack}" verbose="true"/>
+
+ <echo message="Downloading CVS client"/>
+ <get dest="${target.platform}/cvsclient.zip"
+ src="${download.base}/${download.dir}/${download.cvsclient}" verbose="true"/>
+
+ <unzip src="${target.platform}/platform-runtime.zip" dest="${target.platform}" overwrite="true"/>
+ <unzip src="${target.platform}/deltapack.zip" dest="${target.platform}" overwrite="true"/>
+ <unzip src="${target.platform}/cvsclient.zip" dest="${target.platform}" overwrite="true"/>
+ </target>
+ -->
+
+ <target name="copy.projects" depends="init">
+ <foreach list="${plugins}" param="plugin.id" target="__copy.plugin"/>
+ <foreach list="${features}" param="feature.id" target="__copy.feature"/>
+ </target>
+
+ <target name="copy.properties" depends="init">
+ <copy file="builder/template.build.properties" tofile="builder/build.properties" overwrite="true"/>
+ <pathconvert property="basedir.unix" targetos="unix">
+ <path location="${basedir}"/>
+ </pathconvert>
+ <replace file="builder/build.properties">
+ <replacefilter token="@@WARNING_TEXT" value="Warning! This file is auto-generated. Changes will be overwritten!"/>
+ <replacefilter token="@@BUILD_PROJECT_DIR" value="${basedir.unix}"/>
+ <replacefilter token="@@TARGET_PLATFORM" value="${target.platform}"/>
+ </replace>
+ </target>
+
+ <!-- Unused
+ <target name="copy.deps">
+ <copy todir="${target.platform}/eclipse/plugins">
+ <fileset dir="target-libs" includes="*.jar"/>
+ </copy>
+ </target>
+ -->
+
+ <target name="pdebuild" depends="findbundles,copy.projects,copy.properties">
+ <java jar="${eclipse.launcher.jar}" fork="true" failonerror="true">
+ <sysproperty key="builder" path="${basedir}/builder"/>
+ <arg line="-application org.eclipse.ant.core.antRunner"/>
+ <arg line="-buildfile ${eclipse.featureBuild.xml}"/>
+ </java>
+ </target>
+
+ <target name="new.updateSite">
+ <copy file="site.xml" todir="${updateSiteDir}"/>
+ <antcall target="copy.updateSite" />
+ </target>
+
+ <target name="copy.updateSite" depends="custom.tasks">
+ <copy todir="${updateSiteDir}">
+ <fileset dir="target/tmp/eclipse" includes="**/*.jar"/>
+ </copy>
+ <property file="target/finalFeaturesVersions.properties" prefix="featureVersions"/>
+ <siteInsertFeatures sitexmlfile="${updateSiteDir}/site.xml"
+ features="${features}"
+ versionPropPrefix="featureVersions"
+ categoryPropPrefix="category"/>
+ </target>
+
+ <!-- INTERNAL TARGETS -->
+ <target name="__copy.plugin">
+ <fail unless="plugin.id" message="${error.internal.target}"/>
+ <echo message="Copying plugin ${plugin.id}"/>
+ <antcall target="__copy.artifact">
+ <param name="artifact.id" value="${plugin.id}"/>
+ <param name="artifact.type" value="plugin"/>
+ </antcall>
+ </target>
+
+ <target name="__copy.feature">
+ <fail unless="feature.id" message="${error.internal.target}"/>
+ <antcall target="__copy.artifact">
+ <param name="artifact.id" value="${feature.id}"/>
+ <param name="artifact.type" value="feature"/>
+ </antcall>
+
+ <replace file="target/features/${feature.id}/feature.xml">
+ <replacefilter token="http://replace.with.real.url" value="${updateUrl}"/>
+ </replace>
+ </target>
+
+ <target name="__copy.artifact">
+ <fail unless="artifact.id" message="${error.internal.target}"/>
+ <fail unless="artifact.type" message="${error.internal.target}"/>
+
+ <echo message="Copying from ${source.dir}/${artifact.id} to target/${artifact.type}s"/>
+ <copy todir="target/${artifact.type}s">
+ <fileset dir="${source.dir}">
+ <exclude name="${artifact.id}/bin/**"/>
+ <exclude name="${artifact.id}/build/**"/>
+ <exclude name="${artifact.id}/**/*.class"/>
+ <include name="${artifact.id}/**"/>
+ </fileset>
+ </copy>
+ </target>
+
+ <target name="__nodefault">
+ <fail message="There is no default target"/>
+ </target>
+
+ <!-- TASK DEFINITIONS -->
+ <taskdef resource="net/sf/antcontrib/antlib.xml">
+ <classpath>
+ <pathelement location="build-libs/ant-contrib/ant-contrib-1.0b3.jar"/>
+ </classpath>
+ </taskdef>
+
+</project>
diff --git a/sigil/sigil-builder/builder/template.build.properties b/sigil/sigil-builder/builder/template.build.properties
new file mode 100644
index 0000000..a097442
--- /dev/null
+++ b/sigil/sigil-builder/builder/template.build.properties
@@ -0,0 +1,215 @@
+###############################################################################
+#
+# @@WARNING_TEXT
+#
+############# WHAT SHOULD WE BUILD? #############
+
+#To build a Product
+#product=/org.blah.blah/blah.product
+#runPackager=true
+
+#To build a Feature
+topLevelElementId=org.cauldron.sigil.all.feature
+topLevelElementType=feature
+
+#The prefix that will be used in the generated archive.
+archivePrefix=eclipse
+
+# The location underwhich all of the build output will be collected.
+collectingFolder=${archivePrefix}
+
+# The list of {os, ws, arch} configurations to build. This
+# value is a '&' separated list of ',' separate triples. For example,
+# configs=win32,win32,x86 & linux,motif,x86
+# By default the value is *,*,*
+configs=\
+ *, *, *
+# win32, win32, x86
+# macosx, carbon, ppc &\
+# linux, gtk, ppc &\
+# linux, gtk, x86 & \
+# linux, gtk, x86_64 & \
+# linux, motif, x86 & \
+# solaris, motif, sparc & \
+# solaris, gtk, sparc & \
+# aix, motif, ppc & \
+# hpux, motif, PA_RISC & \
+# macosx, carbon, ppc
+
+# By default PDE creates one archive (result) per entry listed in the configs property.
+# Setting this value to try will cause PDE to only create one output containing all
+# artifacts for all the platforms listed in the configs property.
+#groupConfigurations=true
+
+#The format of the archive. By default a zip is created using antZip.
+#The list can only contain the configuration for which the desired format is different than zip.
+archivesFormat=*,*,* - folder
+#archivesFormat=win32, win32, x86 - antZip& \
+# linux, gtk, ppc - antZip &\
+# linux, gtk, x86 - antZip& \
+# linux, gtk, x86_64 - antZip& \
+# linux, motif, x86 - antZip& \
+# solaris, motif, sparc - antZip& \
+# solaris, gtk, sparc - antZip& \
+# aix, motif, ppc - antZip& \
+# hpux, motif, PA_RISC - antZip& \
+# macosx, carbon, ppc - antZip
+
+#Set to true if you want the output to be ready for an update jar (no site.xml generated)
+outputUpdateJars=true
+
+#Set to true for Jnlp generation
+#codebase should be a URL that will be used as the root of all relative URLs in the output.
+#generateJnlp=false
+#jnlp.codebase=<codebase url>
+#jnlp.j2se=<j2se version>
+#jnlp.locale=<a locale>
+#jnlp.generateOfflineAllowed=true or false generate <offlineAllowed/> attribute in the generated features
+#jnlp.configs=${configs} #uncomment to filter the content of the generated jnlp files based on the configuration being built
+
+#Set to true if you want to sign jars
+#signJars=false
+#sign.alias=<alias>
+#sign.keystore=<keystore location>
+#sign.storepass=<keystore password>
+
+#Arguments to send to the zip executable
+zipargs=
+
+#Arguments to send to the tar executable
+tarargs=
+
+#Control the creation of a file containing the version included in each configuration - on by default
+#generateVersionsLists=false
+
+############## BUILD NAMING CONTROL ################
+# The directory into which the build elements are fetched and where
+# the build takes place.
+buildProjectDirectory=@@BUILD_PROJECT_DIR
+buildDirectory=${buildProjectDirectory}/target
+
+# Type of build. Used in naming the build output. Typically this value is
+# one of I, N, M, S, ...
+buildType=N
+
+# ID of the build. Used in naming the build output.
+buildId=Sigil
+
+# Label for the build. Used in naming the build output
+buildLabel=${buildType}.${buildId}
+
+# Timestamp for the build. Used in naming the build output
+timestamp=007
+
+#The value to be used for the qualifier of a plugin or feature when you want to override the value computed by pde.
+#The value will only be applied to plugin or features indicating build.properties, qualifier = context
+#forceContextQualifier=<the value for the qualifier>
+
+#Enable / disable the generation of a suffix for the features that use .qualifier.
+#The generated suffix is computed according to the content of the feature
+#generateFeatureVersionSuffix=true
+
+############# BASE CONTROL #############
+# Settings for the base Eclipse components and Java class libraries
+# against which you are building.
+# Base location for anything the build needs to compile against. For example,
+# in most RCP app or a plug-in, the baseLocation should be the location of a previously
+# installed Eclipse against which the application or plug-in code will be compiled and the RCP delta pack.
+
+base=@@TARGET_PLATFORM
+baseLocation=${base}/eclipse
+#Os/Ws/Arch/nl of the eclipse specified by baseLocation
+baseos=win32
+basews=win32
+basearch=x86
+
+#this property indicates whether you want the set of plug-ins and features to be considered during the build to be limited to the ones reachable from the features / plugins being built
+filteredDependencyCheck=false
+
+#this property indicates whether the resolution should be done in development mode (i.e. ignore multiple bundles with singletons)
+resolution.devMode=false
+
+#pluginPath is a list of locations in which to find plugins and features. This list is separated by the platform file separator (; or :)
+#a location is one of:
+#- the location of the jar or folder that is the plugin or feature : /path/to/foo.jar or /path/to/foo
+#- a directory that contains a /plugins or /features subdirectory
+#- the location of a feature.xml, or for 2.1 style plugins, the plugin.xml or fragment.xml
+#pluginPath=
+
+skipBase=true
+eclipseURL=http://www.eclipse.org/downloads/
+eclipseBuildId=3.3RC2
+eclipseBaseURL=${eclipseURL}/eclipse-platform-${eclipseBuildId}-win32.zip
+
+
+############# MAP FILE CONTROL ################
+# This section defines CVS tags to use when fetching the map files from the repository.
+# If you want to fetch the map file from repository / location, change the getMapFiles target in the customTargets.xml
+
+skipMaps=true
+mapsRepo=:pserver:anonymous@example.com/path/to/repo
+mapsRoot=path/to/maps
+mapsCheckoutTag=HEAD
+
+#tagMaps=true
+mapsTagTag=v${buildId}
+
+
+############ REPOSITORY CONTROL ###############
+# This section defines properties parameterizing the repositories where plugins, fragments
+# bundles and features are being obtained from.
+
+# The tags to use when fetching elements to build.
+# By default thebuilder will use whatever is in the maps.
+# This value takes the form of a comma separated list of repository identifier (like used in the map files) and the
+# overriding value
+# For example fetchTag=CVS=HEAD, SVN=v20050101
+# fetchTag=HEAD
+skipFetch=true
+
+
+############# JAVA COMPILER OPTIONS ##############
+# The location of the Java jars to compile against. Typically the rt.jar for your JDK/JRE
+#bootclasspath=${java.home}/lib/rt.jar
+
+# specific JRE locations to compile against. These values are used to compile bundles specifying a
+# Bundle-RequiredExecutionEnvironment. Uncomment and set values for environments that you support
+#CDC-1.0/Foundation-1.0= /path/to/rt.jar
+#CDC-1.1/Foundation-1.1=
+#OSGi/Minimum-1.0=
+#OSGi/Minimum-1.1=
+#JRE-1.1=
+#J2SE-1.2=
+#J2SE-1.3=
+#J2SE-1.4=
+#J2SE-1.5=
+#JavaSE-1.6=
+#PersonalJava-1.1=
+#PersonalJava-1.2=
+#CDC-1.0/PersonalBasis-1.0=
+#CDC-1.0/PersonalJava-1.0=
+#CDC-1.1/PersonalBasis-1.1=
+#CDC-1.1/PersonalJava-1.1=
+
+# Specify the output format of the compiler log when eclipse jdt is used
+logExtension=.log
+
+# Whether or not to include debug info in the output jars
+javacDebugInfo=true
+
+# Whether or not to fail the build if there are compiler errors
+javacFailOnError=true
+
+# Enable or disable verbose mode of the compiler
+javacVerbose=true
+
+# Extra arguments for the compiler. These are specific to the java compiler being used.
+#compilerArg=
+
+# Default value for the version of the source code. This value is used when compiling plug-ins that do not set the Bundle-RequiredExecutionEnvironment or set javacSource in build.properties
+javacSource=1.5
+
+# Default value for the version of the byte code targeted. This value is used when compiling plug-ins that do not set the Bundle-RequiredExecutionEnvironment or set javacTarget in build.properties.
+javacTarget=1.5
+
+
diff --git a/sigil/sigil-builder/download.properties b/sigil/sigil-builder/download.properties
new file mode 100644
index 0000000..fd21601
--- /dev/null
+++ b/sigil/sigil-builder/download.properties
@@ -0,0 +1,10 @@
+# For UK mirror service
+download.base=http://www.mirrorservice.org/sites/download.eclipse.org/eclipseMirror/eclipse/downloads/drops/
+
+# 3.3.1.1 Release
+download.dir=R-3.3.1.1-200710231652
+
+#
+download.platform.runtime=eclipse-platform-3.3.1.1-win32.zip
+download.deltapack=eclipse-RCP-3.3.1.1-delta-pack.zip
+download.cvsclient=eclipse-CVS-Client-3.3.1.1.zip
\ No newline at end of file
diff --git a/sigil/sigil-builder/project.properties b/sigil/sigil-builder/project.properties
new file mode 100644
index 0000000..b244e72
--- /dev/null
+++ b/sigil/sigil-builder/project.properties
@@ -0,0 +1,18 @@
+# The list of plugins to be build
+plugins=org.cauldron.sigil.core,\
+ org.cauldron.sigil.obr,\
+ org.cauldron.sigil.search,\
+ org.cauldron.sigil.ui,\
+ org.cauldron.sigil.help,\
+ org.cauldron.sigil.utils,\
+ org.cauldron.bld.core,\
+ org.cauldron.bld.obr
+
+# The list of features to be built
+features=org.cauldron.sigil.all.feature,\
+ org.cauldron.sigil.feature,\
+ org.cauldron.sigil.obr.feature
+
+# Mapping of features to categories in the Update Site
+category.org.cauldron.sigil.feature=Sigil
+category.org.cauldron.sigil.obr.feature=Sigil
diff --git a/sigil/sigil-builder/readme.html b/sigil/sigil-builder/readme.html
new file mode 100644
index 0000000..2427241
--- /dev/null
+++ b/sigil/sigil-builder/readme.html
@@ -0,0 +1,131 @@
+<html>
+<head>
+<title>Sigil Build Instructions</title>
+</head>
+<body>
+
+<h1>Build Instructions</h1>
+
+<h2>1. Install Prerequisites</h2>
+
+<p>A full copy of Eclipse SDK is required to build. This must be
+installed in a directory called <code>eclipse</code>, and the full path
+should not contain any spaces. For example:</p>
+
+<ul>
+ <li><code>C:/eclipse-SDK-3.4/eclipse</code> is GOOD</li>
+ <li><code>C:/eclipse-SDK-3.4/</code> is BAD</li>
+ <li><code>C:/Program Files/Eclipse 3.4/</code> is VERY BAD</li>
+</ul>
+
+<p>This directory (<strong>including</strong> the final <code>eclipse</code>)
+will be referred to as the SDK directory.</p>
+
+<h2>2. Setup the Target Platform</h2>
+
+<p>The target platform is the collection of binary plugins and
+features against which the source projects are built. It is an Eclipse
+installation (with a <code>plugins</code> and <code>features</code>
+directory), and so can be setup by downloading the correct zip or
+tarball from the Eclipse website. Like the SDK directory, the target
+platform must be inside a directory called <code>eclipse</code>. In your
+IDE, you should point PDE at the target platform by opening Preferences
+and navigating to "Plug-in Development" --> "Target Platform". Click
+Browse and select the <code>eclipse</code> directory.</p>
+
+<p>Note that since we are building SDK plugins, the Target Platform
+will be a full SDK (eg "Eclipse Classic SDK"). Therefore we can reuse
+the same SDK directory that we setup in the previous step. However it
+can also be useful to separate these because we might want to use
+different Eclipse versions for each function. For example, we may want
+to use Eclipse 3.4 to build a 3.3-based product.</p>
+
+<h2>3. Create and edit <code>user.properties</code></h2>
+
+<p>User-specific properties are supplied in the file <code>user.properties</code>.
+After checkout this file does not exist, so please copy <code>template.user.properties</code>
+to <code>user.properties</code> and change the following settings:</p>
+
+<ul>
+ <li><code>target.platform</code>: this should be set to the <strong>parent
+ </strong> directory of the <code>eclipse</code> directory which contains the
+ Target Platform.</li>
+ <li><code>eclipse.install.dir</code>: the SDK directory (including
+ final <code>eclipse</code> segment).</li>
+</ul>
+
+<p>Do not check <code>user.properties</code> into version control:
+the settings inside are specific to your machine.</p>
+
+<h2>4. Configure the Update Site URL</h2>
+
+<p>The Update Site URL can be specified by setting the <code>updateUrl</code>
+property in <code>project.properties</code> . This will be copied into
+the <code>feature.xml</code> descriptor for each feature that is part of
+the build. For this to work, the update site URL setting in the <code>feature.xml</code>
+of each source feature project must be set to the following placeholder
+string: <code>http://replace.with.real.url</code></p>
+
+<h2>5. Execute the Build</h2>
+
+<p>Execute a full build by running the <code>pdebuild</code> target
+in ANT, e.g.:</p>
+
+<pre>
+ ant pdebuild
+</pre>
+
+<p>This will generate a directory named <code>target/tmp/eclipse</code>
+which contains a <code>plugins</code> directory for the built plugin
+JARs and a <code>features</code> directory for the built features. If
+the build fails then compilation logs for each plugin can be found in <code>target/N.Sigil/compilelogs/plugins</code>.</p>
+
+<h2>6. Deploy to Update Site</h2>
+
+<p>After the features and plugins are built we can deploy them to an
+update site on the local filesystem. The update site directory must
+already exist with the following layout:</p>
+
+<pre>
+${updateSiteDir}/
+ |--plugins/
+ |--features/
+ \--site.xml
+</pre>
+
+<p>And <code>site.xml</code> must have at least the following
+content:</p>
+
+<pre>
+<site>
+ <category-def name="Sigil" label="Sigil">
+ <description>Blah blah</description>
+ </category-def>
+</site>
+</pre>
+
+<p>It can also have any number of pre-existing <code><feature></code>
+entries as direct children of the <code><site></code> element.</p>
+
+<p>Next set the <code>updateSiteDir</code> property in <code>project.properties</code>
+to point at the above directory. Now we can run the deploy target as
+follows:</p>
+
+<pre>
+ ant copy.updateSite
+</pre>
+
+<p>This will copy all of the plugin and feature JARs that were built
+in the previous step into the update site directory. It will also add a
+new entry into <code>site.xml</code> for each feature. Note that the
+update site directory will grow continually. Since the plugins and
+features are generated with a timestamp in their version qualifier (e.g.
+1.2.3.200808091200 for version 1.2.3 built on 9 Aug 2008 at 12 o'clock
+GMT), Eclipse Update Manager will always select the newest version
+available.</p>
+
+<p>NB: the deploy target will overwrite <code>site.xml</code>. A
+backup will be saved in <code>site.xml.bak</code> but this backup will
+be overwritten next time the build is run.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/sigil/sigil-builder/server-nightly.properties b/sigil/sigil-builder/server-nightly.properties
new file mode 100644
index 0000000..c049a4a
--- /dev/null
+++ b/sigil/sigil-builder/server-nightly.properties
@@ -0,0 +1,6 @@
+eclipse.install.dir=/opt/eclipse-SDK-3.4/eclipse
+source.dir=${basedir}/..
+target.platform=target/target-platform
+updateSiteDir=target/update-site
+# updateUrl is over-ridden from Hudson
+updateUrl=http://sigil.codecauldron.org/update-site-daily
diff --git a/sigil/sigil-builder/site.xml b/sigil/sigil-builder/site.xml
new file mode 100644
index 0000000..5ad74f3
--- /dev/null
+++ b/sigil/sigil-builder/site.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<site>
+ <category-def name="Sigil" label="Sigil Core">
+ <description>
+ Sigil is an SDK for developing applications to be deployed on the Newton framework (http://newton.codecauldron.org)
+ </description>
+ </category-def>
+</site>
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java
new file mode 100644
index 0000000..6f4378a
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/Feature.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ *
+ */
+package org.cauldron.sigil.build;
+
+class Feature {
+ String id, version, url;
+ String[] categories;
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java
new file mode 100644
index 0000000..0f7b4a2
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/FindBundlesTask.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.osgi.framework.Version;
+
+public class FindBundlesTask extends Task {
+
+ private File dir;
+ private String symbolicName;
+ private String property;
+
+ public File getDir() {
+ return dir;
+ }
+
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ public void setSymbolicName(String symbolicName) {
+ this.symbolicName = symbolicName;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ System.out.println("Searching " + dir + " for bundle '" + symbolicName + "'");
+ final String prefix = symbolicName + "_";
+ String[] files = dir.list(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.startsWith(prefix);
+ }
+ });
+ if (files == null)
+ files = new String[0];
+
+ System.out.println("Found " + files.length + " file(s) starting with " + symbolicName);
+
+ Version highest = null;
+ for (String filename : files) {
+ System.out.println("Testing " + filename);
+ // Drop the prefix
+ int startIndex = prefix.length();
+
+ // Drop the ".jar" suffix if present
+ int endIndex = filename.length();
+ if (filename.toLowerCase().endsWith(".jar")) {
+ endIndex -= 4;
+ }
+
+ String versionString = filename.substring(startIndex, endIndex);
+ System.out.println("Version string is '" + versionString + "'");
+
+ Version version = new Version(versionString);
+ if (highest == null || version.compareTo(highest) > 0) {
+ highest = version;
+ }
+ }
+
+ if (highest == null) {
+ throw new BuildException("No matches for symbolic name '"
+ + symbolicName + "'");
+ }
+
+ getProject().setNewProperty(property, highest.toString());
+ }
+
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java
new file mode 100644
index 0000000..bf710a7
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatureContentHandler.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.util.List;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+class SiteInsertFeatureContentHandler implements ContentHandler {
+
+ private final ContentHandler output;
+ private final List<org.cauldron.sigil.build.Feature> featureList;
+
+ public SiteInsertFeatureContentHandler(ContentHandler output,
+ List<Feature> featureList) {
+ this.output = output;
+ this.featureList = featureList;
+ }
+
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ output.characters(ch, start, length);
+ }
+
+ public void endDocument() throws SAXException {
+ output.endDocument();
+ }
+
+ public void endElement(String uri, String localName, String name) throws SAXException {
+ output.endElement(uri, localName, name);
+ }
+
+ public void endPrefixMapping(String prefix) throws SAXException {
+ output.endPrefixMapping(prefix);
+ }
+
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ //output.ignorableWhitespace(ch, start, length);
+ }
+
+ public void processingInstruction(String target, String data) throws SAXException {
+ output.processingInstruction(target, data);
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ output.setDocumentLocator(locator);
+ }
+
+ public void skippedEntity(String name) throws SAXException {
+ output.skippedEntity(name);
+ }
+
+ public void startDocument() throws SAXException {
+ output.startDocument();
+ }
+
+ public void startElement(String uri, String localName, String name, Attributes atts)
+ throws SAXException {
+ output.startElement(uri, localName, name, atts);
+
+ if("site".equals(name)) {
+ for (Feature feature : featureList) {
+ AttributesImpl featureAtts = new AttributesImpl();
+ featureAtts.addAttribute("", "", "url", "CDATA", feature.url);
+ featureAtts.addAttribute("", "", "id", "CDATA", feature.id);
+ featureAtts.addAttribute("", "", "version", "CDATA", feature.version);
+ output.startElement("", "", "feature", featureAtts);
+
+ for (int i = 0; i < feature.categories.length; i++) {
+ AttributesImpl categoryAtts = new AttributesImpl();
+ categoryAtts.addAttribute("", "", "name", "CDATA", feature.categories[i]);
+ output.startElement("", "", "category", categoryAtts);
+ output.endElement("", "", "category");
+ }
+
+ output.endElement("", "", "feature");
+ }
+ }
+ }
+
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ output.startPrefixMapping(prefix, uri);
+ }
+
+}
diff --git a/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java
new file mode 100644
index 0000000..9d09008
--- /dev/null
+++ b/sigil/sigil-builder/src/org/cauldron/sigil/build/SiteInsertFeatures.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.cauldron.sigil.build;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+public class SiteInsertFeatures extends Task {
+
+ private File siteXmlFile;
+ private String features;
+ private String versionPropPrefix;
+ private String categoryPropPrefix;
+
+ public File getSiteXmlFile() {
+ return siteXmlFile;
+ }
+ public void setSiteXmlFile(File siteXmlFile) {
+ this.siteXmlFile = siteXmlFile;
+ }
+ public String getFeatures() {
+ return features;
+ }
+ public void setFeatures(String features) {
+ this.features = features;
+ }
+ public String getVersionPropPrefix() {
+ return versionPropPrefix;
+ }
+ public void setVersionPropPrefix(String versionPropPrefix) {
+ this.versionPropPrefix = versionPropPrefix;
+ }
+ public String getCategoryPropPrefix() {
+ return categoryPropPrefix;
+ }
+ public void setCategoryPropPrefix(String categoryPropPrefix) {
+ this.categoryPropPrefix = categoryPropPrefix;
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ Project project = getProject();
+
+ List<Feature> featureList = new ArrayList<Feature>();
+ StringTokenizer tokenizer = new StringTokenizer(features, ",");
+ while(tokenizer.hasMoreTokens()) {
+ Feature feature = new Feature();
+ feature.id = tokenizer.nextToken().trim();
+
+ // Find the version property
+ String versionProp;
+ if(versionPropPrefix == null) {
+ versionProp = feature.id;
+ } else {
+ versionProp = versionPropPrefix + "." + feature.id;
+ }
+ feature.version = project.getProperty(versionProp);
+
+ // Find the categories for this feature
+ feature.categories = new String[0];
+ if(categoryPropPrefix != null) {
+ String categoriesStr = project.getProperty(categoryPropPrefix + "." + feature.id);
+ if(categoriesStr != null) {
+ StringTokenizer categoriesTokenizer = new StringTokenizer(categoriesStr, ",");
+ feature.categories = new String[categoriesTokenizer.countTokens()];
+ for(int i=0; i<feature.categories.length; i++) {
+ feature.categories[i] = categoriesTokenizer.nextToken();
+ }
+ }
+ }
+
+ if(feature.version != null) {
+ feature.url = "features/" + feature.id + "_" + feature.version + ".jar";
+ featureList.add(feature);
+ } else {
+ System.out.println("Skipping feature " + feature.id);
+ }
+ }
+
+ if(!siteXmlFile.isFile()) {
+ throw new BuildException(siteXmlFile + " does not exist or is not a normal file");
+ }
+ try {
+ // Generate new XML into a temporary file
+ File tempFile = File.createTempFile("tmp", ".xml", siteXmlFile.getParentFile());
+ tempFile.deleteOnExit();
+
+ SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+ transformerHandler.setResult(new StreamResult(tempFile));
+
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ SAXParser parser = parserFactory.newSAXParser();
+
+ SiteInsertFeatureContentHandler contentHandler = new SiteInsertFeatureContentHandler(transformerHandler, featureList);
+
+ XMLReader reader = parser.getXMLReader();
+ reader.setContentHandler(contentHandler);
+ reader.parse(new InputSource(new FileInputStream(siteXmlFile)));
+
+ // Backup original file
+ File backup = new File(siteXmlFile.getParentFile(), siteXmlFile.getName() + ".bak");
+ copyFile(siteXmlFile, backup);
+
+ // Replace original file
+ copyFile(tempFile, siteXmlFile);
+
+ } catch (IOException e) {
+ throw new BuildException(e);
+ } catch (TransformerConfigurationException e) {
+ throw new BuildException(e);
+ } catch (IllegalArgumentException e) {
+ throw new BuildException(e);
+ } catch (TransformerFactoryConfigurationError e) {
+ throw new BuildException(e);
+ } catch (ParserConfigurationException e) {
+ throw new BuildException(e);
+ } catch (SAXException e) {
+ throw new BuildException(e);
+ }
+ }
+
+ private void copyFile(File source, File dest) throws IOException {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+ try {
+ in = new FileInputStream(source);
+ out = new FileOutputStream(dest);
+
+ byte[] buffer = new byte[1024];
+
+ int read;
+ while((read = in.read(buffer, 0, 1024)) > -1) {
+ out.write(buffer, 0, read);
+ }
+ } finally {
+ try { if(in != null) in.close(); } catch(IOException e) {}
+ try { if(out != null) out.close(); } catch(IOException e) {}
+ }
+
+ }
+}
diff --git a/sigil/sigil-builder/template.user.properties b/sigil/sigil-builder/template.user.properties
new file mode 100644
index 0000000..ed14a54
--- /dev/null
+++ b/sigil/sigil-builder/template.user.properties
@@ -0,0 +1,14 @@
+#
+source.dir=${basedir}/..
+
+#
+target.platform=/path/to/target
+
+# path to eclipse sdk
+eclipse.install.dir=/path/to/sdk/eclipse
+
+# The URL of the Update Site, for insertion into each feature.xml
+updateUrl=http://sigil.codecauldron.org/update-site-nightly
+
+# The path to an Update Site directory on the local filesystem
+updateSiteDir=/opt/development/sigil-trunk/update-site-nightly