/*******************************************************************************
* Copyright © 2000, 2013 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.edt.ide.core.internal.search;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.ResourcesPlugin;
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.edt.ide.core.model.EGLCore;
import org.eclipse.edt.ide.core.model.EGLModelException;
import org.eclipse.edt.ide.core.model.ElementChangedEvent;
import org.eclipse.edt.ide.core.model.IEGLElement;
import org.eclipse.edt.ide.core.model.IEGLElementDelta;
import org.eclipse.edt.ide.core.model.IEGLFile;
import org.eclipse.edt.ide.core.model.IElementChangedListener;
import org.eclipse.edt.ide.core.model.IIndexConstants;
import org.eclipse.edt.ide.core.model.IPart;
import org.eclipse.edt.ide.core.model.Signature;
import org.eclipse.edt.ide.core.search.IEGLSearchConstants;
import org.eclipse.edt.ide.core.search.IEGLSearchScope;
import org.eclipse.edt.ide.core.search.IPartNameRequestor;
import org.eclipse.edt.ide.core.search.SearchEngine;
/**
* Manages a search cache for types in the workspace. Instead of returning objects of type <code>IPart</code>
* the methods of this class returns a list of the lightweight objects <code>PartInfo</code>.
*/
public class AllPartsCache {
private static PartInfo[] fgTypeCache= null;
private static int fgSizeHint= 2000;
private static int fgNumberOfCacheFlushes= 0;
private static class PartNameComparator implements Comparator {
public int compare(Object o1, Object o2) {
return ((PartInfo)o1).getPartName().compareTo(((PartInfo)o2).getPartName());
}
}
private static Comparator fgPartNameComparator= new PartNameComparator();
/**
* Returns all types in the given scope.
* @param kind IEGLSearchConstants.CLASS, IEGLSearchConstants.INTERFACE
* or IEGLSearchConstants.PART
* @param typesFound The resulting <code>PartInfo</code> elements are added to this collection
*/
public static void getParts(IEGLSearchScope scope, int kind, IProgressMonitor monitor, Collection partsFound) throws EGLModelException {
getParts(scope, kind, null, monitor, partsFound);
}
private static void addPartToCollection(IEGLSearchScope scope, PartInfo info, Collection partsFound, int kind, String subType) throws EGLModelException
{
if((info.getPartType() & kind) != 0)
{
if(subType != null && subType.length()>0)
{
IPart part = info.resolvePart(scope);
if(part != null && part.getSubTypeSignature() != null)
{
String partSubTypeString = Signature.toString(part.getSubTypeSignature());
if(partSubTypeString.equalsIgnoreCase(subType))
partsFound.add(info);
}
}
else //ignore the subType
{
partsFound.add(info);
}
}
}
/**
* Returns all types in the given scope.
* @param scope
* @param kind IEGLSearchConstants.CLASS, IEGLSearchConstants.INTERFACE
* or IEGLSearchConstants.PART
* @param subType the subType String, i.e. EGLInterfaceType.EGL_JAVAOBJECT_INSTANCE.getName()
* @param monitor
* @param partsFound
* @throws EGLModelException
*/
public static void getParts(IEGLSearchScope scope, int kind, String subType, IProgressMonitor monitor, Collection partsFound) throws EGLModelException {
PartInfo[] allParts= getAllParts(monitor);
boolean isWorkspaceScope= scope.equals(SearchEngine.createWorkspaceScope());
for (int i= 0; i < allParts.length; i++) {
PartInfo info= (PartInfo) fgTypeCache[i];
if (isWorkspaceScope || info.isEnclosed(scope)) {
addPartToCollection(scope, info, partsFound, kind, subType);
}
}
}
/**
* Returns all types in the workspace. The returned array must not be
* modified. The elements in the array are sorted by simple type name.
*/
public static synchronized PartInfo[] getAllParts(IProgressMonitor monitor) throws EGLModelException {
if (fgTypeCache == null) {
ArrayList searchResult= new ArrayList(fgSizeHint);
doSearchParts(SearchEngine.createWorkspaceScope(), IEGLSearchConstants.PART, monitor, searchResult);
if (monitor != null && monitor.isCanceled()) {
return null;
}
monitor= null; // prevents duplicated invocation of monitor.done
fgTypeCache= (PartInfo[]) searchResult.toArray(new PartInfo[searchResult.size()]);
Arrays.sort(fgTypeCache, getPartNameComperator());
fgSizeHint= fgTypeCache.length;
EGLCore.addElementChangedListener(new PartCacheDeltaListener());
}
if (monitor != null) {
monitor.done();
}
return fgTypeCache;
}
/**
* Returns true if the type cache is up to date.
*/
public static boolean isCacheUpToDate() {
return fgTypeCache != null;
}
/**
* Returns a hint for the number of all types in the workspace.
*/
public static int getNumberOfAllPartsHint() {
return fgSizeHint;
}
/**
* Returns a compartor that compares the simple type names
*/
public static Comparator getPartNameComperator() {
return fgPartNameComparator;
}
private static void doSearchParts(IEGLSearchScope scope, int style, IProgressMonitor monitor, Collection typesFound) throws EGLModelException {
new SearchEngine().searchAllPartNames(ResourcesPlugin.getWorkspace(),
null,
null,
IIndexConstants.PATTERN_MATCH,
IEGLSearchConstants.CASE_INSENSITIVE,
style,
scope,
new PartInfoRequestor(typesFound),
IEGLSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
monitor);
}
private static class PartCacheDeltaListener implements IElementChangedListener {
/*
* @see IElementChangedListener#elementChanged
*/
public void elementChanged(ElementChangedEvent event) {
boolean needsFlushing= processDelta(event.getDelta());
if (needsFlushing) {
fgTypeCache= null;
fgNumberOfCacheFlushes++;
EGLCore.removeElementChangedListener(this); // it's ok to remove listener while delta processing
}
}
/*
* returns true if the cache needs to be flushed
*/
private boolean processDelta(IEGLElementDelta delta) {
IEGLElement elem= delta.getElement();
boolean isAddedOrRemoved= (delta.getKind() != IEGLElementDelta.CHANGED)
|| (delta.getFlags() & (IEGLElementDelta.F_ADDED_TO_EGLPATH | IEGLElementDelta.F_REMOVED_FROM_EGLPATH)) != 0;
switch (elem.getElementType()) {
case IEGLElement.EGL_MODEL:
case IEGLElement.EGL_PROJECT:
case IEGLElement.PACKAGE_FRAGMENT_ROOT:
case IEGLElement.PACKAGE_FRAGMENT:
case IEGLElement.CLASS_FILE:
case IEGLElement.PART: // type children can be inner classes
if (isAddedOrRemoved) {
return true;
}
return processChildrenDelta(delta);
case IEGLElement.EGL_FILE: // content change means refresh from local
if (((IEGLFile) elem).isWorkingCopy()) {
return false;
}
if (isAddedOrRemoved || isPossibleStructuralChange(delta.getFlags())) {
return true;
}
return processChildrenDelta(delta);
default:
// fields, methods, imports ect
return false;
}
}
private boolean isPossibleStructuralChange(int flags) {
return (flags & (IEGLElementDelta.F_CONTENT | IEGLElementDelta.F_FINE_GRAINED)) == IEGLElementDelta.F_CONTENT;
}
private boolean processChildrenDelta(IEGLElementDelta delta) {
IEGLElementDelta[] children= delta.getAffectedChildren();
for (int i= 0; i < children.length; i++) {
if (processDelta(children[i])) {
return true;
}
}
return false;
}
}
/**
* Gets the number of times the cache was flushed. Used for testing.
* @return Returns a int
*/
public static int getNumberOfCacheFlushes() {
return fgNumberOfCacheFlushes;
}
/**
* Returns all types for a given name in the scope. The elements in the array are sorted by simple type name.
*/
public static PartInfo[] getPartsForName(String simplePartName, IEGLSearchScope searchScope, IProgressMonitor monitor) throws EGLModelException {
Collection result= new ArrayList();
Set namesFound= new HashSet();
PartInfo[] allParts= AllPartsCache.getAllParts(monitor); // all types in workspace, sorted by type name
PartInfo key= new UnresolvablePartInfo("", simplePartName, null, null); //$NON-NLS-1$
int index= Arrays.binarySearch(allParts, key, AllPartsCache.getPartNameComperator());
if (index >= 0 && index < allParts.length) {
for (int i= index - 1; i>= 0; i--) {
PartInfo curr= allParts[i];
if (simplePartName.equals(curr.getPartName())) {
if (!namesFound.contains(curr.getFullyQualifiedName()) && curr.isEnclosed(searchScope)) {
result.add(curr);
namesFound.add(curr.getFullyQualifiedName());
}
} else {
break;
}
}
for (int i= index; i < allParts.length; i++) {
PartInfo curr= allParts[i];
if (simplePartName.equals(curr.getPartName())) {
if (!namesFound.contains(curr.getFullyQualifiedName()) && curr.isEnclosed(searchScope)) {
result.add(curr);
namesFound.add(curr.getFullyQualifiedName());
}
} else {
break;
}
}
}
return (PartInfo[]) result.toArray(new PartInfo[result.size()]);
}
/**
* Similar to getPartsForName but we include duplicates and filter by part type
*/
public static PartInfo[] getAllPartsForName(String simplePartName, IEGLSearchScope searchScope, IProgressMonitor monitor, int partType) throws EGLModelException {
Collection result= new ArrayList();
PartInfo[] allParts= AllPartsCache.getAllParts(monitor); // all types in workspace, sorted by type name
PartInfo key= new UnresolvablePartInfo("", simplePartName, null, null); //$NON-NLS-1$
int index= Arrays.binarySearch(allParts, key, AllPartsCache.getPartNameComperator());
if (index >= 0 && index < allParts.length) {
for (int i= index - 1; i>= 0; i--) {
PartInfo curr= allParts[i];
if (simplePartName.equals(curr.getPartName())) {
if ((curr.getPartType() & partType) != 0 && curr.isEnclosed(searchScope)) {
result.add(curr);
}
} else {
break;
}
}
for (int i= index; i < allParts.length; i++) {
PartInfo curr= allParts[i];
if (simplePartName.equals(curr.getPartName())) {
if ((curr.getPartType() & partType) != 0 && curr.isEnclosed(searchScope)) {
result.add(curr);
}
} else {
break;
}
}
}
return (PartInfo[]) result.toArray(new PartInfo[result.size()]);
}
/**
* Checks if the search index is up to date.
*/
public static boolean isIndexUpToDate() {
class TypeFoundException extends Error {private static final long serialVersionUID = 1L;}
IPartNameRequestor requestor= new IPartNameRequestor() {
public void acceptPart(char[] packageName, char[] simplePartName, char partType, char[][] enclosingPartNames, String path, IPath projectPath) {
throw new TypeFoundException();
}
};
try {
new SearchEngine().searchAllPartNames(ResourcesPlugin.getWorkspace(),
null,
null,
IIndexConstants.PATTERN_MATCH,
IEGLSearchConstants.CASE_INSENSITIVE,
IEGLSearchConstants.PART,
SearchEngine.createWorkspaceScope(),
requestor,
IEGLSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH,
new NullProgressMonitor());
} catch (EGLModelException e) {
// TODO Log the error EGLPlugin.log(e);
return false;
} catch (OperationCanceledException e) {
return false;
} catch (TypeFoundException e) {
}
return true;
}
public static void flushCache() {
fgNumberOfCacheFlushes += 1;
fgTypeCache = null;
}
}