Initial commit of Sigil contribution. (FELIX-1142)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@793581 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/org.cauldron.sigil.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;
+ }
+
+}