blob: 79ca62369348ee6490b38bc6ee7f9ae271dc089c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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;
}
}
}