blob: cfe9d60c08abaa95d8ca3f858334da75c55fc4d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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
*******************************************************************************/
package org.eclipse.pde.api.tools.internal.search;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.pde.api.tools.internal.model.Reference;
import org.eclipse.pde.api.tools.internal.model.cache.TypeStructureCache;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.ClassFileContainerVisitor;
import org.eclipse.pde.api.tools.internal.provisional.IApiComponent;
import org.eclipse.pde.api.tools.internal.provisional.IClassFile;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
import org.eclipse.pde.api.tools.internal.provisional.model.IReference;
import org.eclipse.pde.api.tools.internal.provisional.search.IApiSearchCriteria;
import org.eclipse.pde.api.tools.internal.provisional.search.IApiSearchEngine;
import org.eclipse.pde.api.tools.internal.provisional.search.IApiSearchResult;
import org.eclipse.pde.api.tools.internal.provisional.search.IApiSearchScope;
import org.eclipse.pde.api.tools.internal.provisional.search.ReferenceModifiers;
import org.eclipse.pde.api.tools.internal.util.Util;
import com.ibm.icu.text.MessageFormat;
/**
* Extracts references from an API component.
*
* @since 1.0.0
*/
public class SearchEngine implements IApiSearchEngine {
/**
* Constant used for controlling tracing in the search engine
*/
private static boolean DEBUG = Util.DEBUG;
/**
* Method used for initializing tracing in the search engine
*/
public static void setDebug(boolean debugValue) {
DEBUG = debugValue || Util.DEBUG;
}
/**
* Empty result collection.
*/
private static final IApiSearchResult[] EMPTY_RESULT = new IApiSearchResult[0];
/**
* Visits each class file, extracting references.
*/
class Visitor extends ClassFileContainerVisitor {
private IApiComponent fCurrentComponent = null;
private IProgressMonitor fMonitor = null;
public Visitor(IProgressMonitor monitor) {
fMonitor = monitor;
}
public void end(IApiComponent component) {
fCurrentComponent = null;
}
public boolean visit(IApiComponent component) {
fCurrentComponent = component;
return true;
}
public boolean visitPackage(String packageName) {
fMonitor.subTask(MessageFormat.format(SearchMessages.SearchEngine_0, new String[]{packageName}));
return true;
}
public void endVisitPackage(String packageName) {
fMonitor.worked(1);
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.model.component.ClassFileContainerVisitor#visit(java.lang.String, org.eclipse.pde.api.tools.model.component.IClassFile)
*/
public void visit(String packageName, IClassFile classFile) {
if (!fMonitor.isCanceled()) {
try {
IApiType type = TypeStructureCache.getTypeStructure(classFile, fCurrentComponent);
List references = type.extractReferences(fAllReferenceKinds, null);
// keep potential matches
Iterator iterator = references.iterator();
while (iterator.hasNext()) {
IReference ref = (IReference) iterator.next();
for (int i = 0; i < fConditions.length; i++) {
if (fConditions[i].isPotentialMatch(ref)) {
fPotentialMatches[i].add(ref);
}
}
}
} catch (CoreException e) {
fStatus.add(e.getStatus());
}
}
}
}
/**
* Scan status
*/
private MultiStatus fStatus;
/**
* Potential matches for each search condition
*/
private List[] fPotentialMatches = null;
/**
* Search criteria
*/
private IApiSearchCriteria[] fConditions = null;
/**
* Mask of all reference kinds to consider based on all search conditions.
*/
private int fAllReferenceKinds = 0;
/**
* Scans the given scope extracting all reference information.
*
* @param scope scope to scan
* @param monitor progress monitor
* @exception CoreException if the scan fails
*/
private void extractReferences(IApiSearchScope scope, IProgressMonitor monitor) throws CoreException {
fStatus = new MultiStatus(ApiPlugin.PLUGIN_ID, 0, SearchMessages.SearchEngine_1, null);
String[] packageNames = scope.getPackageNames();
SubMonitor localMonitor = SubMonitor.convert(monitor, packageNames.length);
ClassFileContainerVisitor visitor = new Visitor(localMonitor);
long start = System.currentTimeMillis();
try {
scope.accept(visitor);
} catch (CoreException e) {
fStatus.add(e.getStatus());
}
long end = System.currentTimeMillis();
if (!fStatus.isOK()) {
throw new CoreException(fStatus);
}
localMonitor.done();
if (DEBUG) {
int size = 0;
for (int i = 0; i < fPotentialMatches.length; i++) {
size += fPotentialMatches[i].size();
}
System.out.println("Search: extracted " + size + " references in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
/**
* Creates a unique string key for a given reference.
* The key is of the form "component X references type/member"
* <pre>
* [component_id]#[type_name](#[member_name]#[member_signature])
* </pre>
* @param reference reference
* @return a string key for the given reference.
*/
private String createSignatureKey(IReference reference) {
StringBuffer buffer = new StringBuffer();
buffer.append(reference.getMember().getApiComponent().getId());
buffer.append("#"); //$NON-NLS-1$
buffer.append(reference.getReferencedTypeName());
switch (reference.getReferenceType()) {
case IReference.T_FIELD_REFERENCE:
buffer.append("#"); //$NON-NLS-1$
buffer.append(reference.getReferencedMemberName());
break;
case IReference.T_METHOD_REFERENCE:
buffer.append("#"); //$NON-NLS-1$
buffer.append(reference.getReferencedMemberName());
buffer.append("#"); //$NON-NLS-1$
buffer.append(reference.getReferencedSignature());
break;
}
return buffer.toString();
}
/**
* Resolves all references.
*
* @param referenceLists lists of {@link IReference} to resolve
* @param progress monitor
* @throws CoreException if something goes wrong
*/
private void resolveReferences(List[] referenceLists, IProgressMonitor monitor) throws CoreException {
// sort references by target type for 'shared' resolution
Map sigtoref = new HashMap(50);
List refs = null;
IReference ref = null;
String key = null;
List methodDecls = new ArrayList(1000);
long start = System.currentTimeMillis();
for (int i = 0; i < referenceLists.length; i++) {
Iterator references = referenceLists[i].iterator();
while (references.hasNext()) {
ref = (IReference) references.next();
if (ref.getReferenceKind() == ReferenceModifiers.REF_OVERRIDE) {
methodDecls.add(ref);
} else {
key = createSignatureKey(ref);
refs = (List) sigtoref.get(key);
if(refs == null) {
refs = new ArrayList(20);
sigtoref.put(key, refs);
}
refs.add(ref);
}
}
}
if (monitor.isCanceled()) {
return;
}
long end = System.currentTimeMillis();
if (DEBUG) {
System.out.println("Search: split into " + methodDecls.size() + " method overrides and " + sigtoref.size() + " unique references (" + (end - start) + "ms)"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
}
// resolve references
start = System.currentTimeMillis();
resolveReferenceSets(sigtoref, monitor);
end = System.currentTimeMillis();
if (DEBUG) {
System.out.println("Search: resolved unique references in " + (end - start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$
}
// resolve method overrides
start = System.currentTimeMillis();
Iterator iterator = methodDecls.iterator();
while (iterator.hasNext()) {
Reference reference = (Reference) iterator.next();
reference.resolve();
}
end = System.currentTimeMillis();
if (DEBUG) {
System.out.println("Search: resolved method overrides in " + (end - start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$
}
}
/**
* Resolves the collect sets of references.
* @param map the mapping of keys to sets of {@link IReference}s
* @throws CoreException if something bad happens
*/
private void resolveReferenceSets(Map map, IProgressMonitor monitor) throws CoreException {
Iterator types = map.keySet().iterator();
String key = null;
List refs = null;
IReference ref= null;
while (types.hasNext()) {
if (monitor.isCanceled()) {
return;
}
key = (String) types.next();
refs = (List) map.get(key);
ref = (IReference) refs.get(0);
((org.eclipse.pde.api.tools.internal.model.Reference)ref).resolve();
IApiMember resolved = ref.getResolvedReference();
if (resolved != null) {
Iterator iterator = refs.iterator();
while (iterator.hasNext()) {
org.eclipse.pde.api.tools.internal.model.Reference ref2 = (org.eclipse.pde.api.tools.internal.model.Reference) iterator.next();
ref2.setResolution(resolved);
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.pde.api.tools.search.IApiSearchEngine#search(org.eclipse.pde.api.tools.search.IApiSearchScope, int[], int[], int[], org.eclipse.pde.api.tools.search.IApiSearchScope, boolean, org.eclipse.core.runtime.IProgressMonitor)
*/
public IApiSearchResult[] search(IApiSearchScope sourceScope,
IApiSearchCriteria[] conditions, IProgressMonitor monitor)
throws CoreException {
SubMonitor localMonitor = SubMonitor.convert(monitor,SearchMessages.SearchEngine_2, 3);
fConditions = conditions;
fPotentialMatches = new List[fConditions.length];
for (int i = 0; i < conditions.length; i++) {
IApiSearchCriteria condition = conditions[i];
fAllReferenceKinds |= condition.getReferenceKinds();
fPotentialMatches[i] = new LinkedList();
}
// 1. extract all references, filtering out kinds we don't care about
localMonitor.subTask(SearchMessages.SearchEngine_3);
extractReferences(sourceScope, localMonitor);
localMonitor.worked(1);
if (localMonitor.isCanceled()) {
return EMPTY_RESULT;
}
// 2. resolve the remaining references
localMonitor.subTask(SearchMessages.SearchEngine_3);
resolveReferences(fPotentialMatches, localMonitor);
localMonitor.worked(1);
if (localMonitor.isCanceled()) {
return EMPTY_RESULT;
}
// 3. filter based on search conditions
localMonitor.subTask(SearchMessages.SearchEngine_3);
int emptyrefs = 0;
for (int i = 0; i < fPotentialMatches.length; i++) {
List references = fPotentialMatches[i];
if (!references.isEmpty()) {
IApiSearchCriteria condition = fConditions[i];
applyConditions(references, condition);
if(references.isEmpty()) {
emptyrefs++;
}
}
else {
emptyrefs++;
}
if (localMonitor.isCanceled()) {
return EMPTY_RESULT;
}
}
int size = fPotentialMatches.length-emptyrefs;
if(size <= 0) {
return EMPTY_RESULT;
}
IApiSearchResult[] results = new IApiSearchResult[size];
int index = 0;
for (int i = 0; i < fPotentialMatches.length; i++) {
List references = fPotentialMatches[i];
if(references.isEmpty()) {
continue;
}
results[index++] = new ApiSearchResult(fConditions[i], (IReference[]) references.toArray(new IReference[references.size()]));
references.clear();
}
fPotentialMatches = null;
localMonitor.worked(1);
localMonitor.done();
return results;
}
/**
* Iterates through the given references, removing those that do not match
* search conditions.
*
* @param references
* @param condition condition to satisfy
*/
private void applyConditions(List references, IApiSearchCriteria condition) {
Iterator iterator = references.iterator();
while (iterator.hasNext()) {
IReference ref = (IReference) iterator.next();
if (!condition.isMatch(ref)) {
iterator.remove();
}
}
}
public void resolveReferences(IReference[] references, IProgressMonitor monitor) throws CoreException {
List list = new ArrayList(references.length);
for (int i = 0; i < references.length; i++) {
list.add(references[i]);
}
resolveReferences(new List[]{list}, monitor);
}
}