| /******************************************************************************* |
| * Copyright (c) 2000, 2017 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.dialogs; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.ProgressMonitorWrapper; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IMethod; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.WorkingCopyOwner; |
| import org.eclipse.dltk.core.index2.search.ModelAccess; |
| import org.eclipse.dltk.core.search.IDLTKSearchConstants; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.MethodNameMatch; |
| import org.eclipse.dltk.core.search.MethodNameMatchRequestor; |
| import org.eclipse.dltk.core.search.NopTypeNameRequestor; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.core.search.SearchPattern; |
| import org.eclipse.dltk.internal.core.search.DLTKSearchMethodNameMatch; |
| import org.eclipse.dltk.internal.corext.util.Messages; |
| import org.eclipse.dltk.internal.corext.util.MethodFilter; |
| import org.eclipse.dltk.internal.corext.util.MethodInfoFilter; |
| import org.eclipse.dltk.internal.corext.util.OpenMethodHistory; |
| import org.eclipse.dltk.internal.corext.util.Strings; |
| import org.eclipse.dltk.internal.ui.DLTKUIMessages; |
| import org.eclipse.dltk.launching.IInterpreterInstall; |
| import org.eclipse.dltk.launching.IInterpreterInstallType; |
| import org.eclipse.dltk.launching.LibraryLocation; |
| import org.eclipse.dltk.launching.ScriptRuntime; |
| import org.eclipse.dltk.ui.DLTKPluginImages; |
| import org.eclipse.dltk.ui.DLTKUILanguageManager; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.IDLTKUILanguageToolkit; |
| import org.eclipse.dltk.ui.ScriptElementImageProvider; |
| import org.eclipse.dltk.ui.ScriptElementLabels; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.MenuAdapter; |
| import org.eclipse.swt.events.MenuEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.ui.progress.UIJob; |
| |
| /** |
| * A viewer to present method queried form the method history and form the |
| * search engine. All viewer updating takes place in the UI thread. Therefore no |
| * synchronization of the methods is necessary. |
| * |
| */ |
| public class MethodInfoViewer { |
| private IDLTKUILanguageToolkit fToolkit; |
| |
| private static class SearchRequestor extends MethodNameMatchRequestor { |
| private volatile boolean fStop; |
| |
| private Set fHistory; |
| |
| private MethodInfoFilter fFilter; |
| private List<MethodNameMatch> fResult; |
| private MethodFilter fMethodFilter; |
| |
| public SearchRequestor(MethodInfoFilter filter, |
| MethodFilter MethodFilter) { |
| super(); |
| fResult = new ArrayList<>(2048); |
| fFilter = filter; |
| fMethodFilter = MethodFilter; |
| } |
| |
| public MethodNameMatch[] getResult() { |
| return fResult.toArray(new MethodNameMatch[fResult.size()]); |
| } |
| |
| public void cancel() { |
| fStop = true; |
| } |
| |
| public void setHistory(Set history) { |
| fHistory = history; |
| } |
| |
| @Override |
| public void acceptMethodNameMatch(MethodNameMatch match) { |
| if (fStop) |
| return; |
| if (fMethodFilter.isFiltered(match)) |
| return; |
| if (fHistory.contains(match)) |
| return; |
| if (fFilter.matchesFilterExtension(match)) |
| fResult.add(match); |
| } |
| } |
| |
| protected static class MethodInfoComparator implements Comparator { |
| private MethodInfoLabelProvider fLabelProvider; |
| private MethodInfoFilter fFilter; |
| |
| public MethodInfoComparator(MethodInfoLabelProvider labelProvider, |
| MethodInfoFilter filter) { |
| fLabelProvider = labelProvider; |
| fFilter = filter; |
| } |
| |
| @Override |
| public int compare(Object left, Object right) { |
| MethodNameMatch leftInfo = (MethodNameMatch) left; |
| MethodNameMatch rightInfo = (MethodNameMatch) right; |
| int leftCategory = getCamelCaseCategory(leftInfo); |
| int rightCategory = getCamelCaseCategory(rightInfo); |
| if (leftCategory < rightCategory) |
| return -1; |
| if (leftCategory > rightCategory) |
| return +1; |
| int result = compareName(leftInfo.getSimpleMethodName(), |
| rightInfo.getSimpleMethodName()); |
| if (result != 0) |
| return result; |
| result = compareTypeContainerName(leftInfo.getMethodContainerName(), |
| rightInfo.getMethodContainerName()); |
| if (result != 0) |
| return result; |
| |
| leftCategory = getElementTypeCategory(leftInfo); |
| rightCategory = getElementTypeCategory(rightInfo); |
| if (leftCategory < rightCategory) |
| return -1; |
| if (leftCategory > rightCategory) |
| return +1; |
| return compareContainerName(leftInfo, rightInfo); |
| } |
| |
| private int compareName(String leftString, String rightString) { |
| int result = leftString.compareToIgnoreCase(rightString); |
| if (result != 0 || rightString.length() == 0) { |
| return result; |
| } else if (Strings.isLowerCase(leftString.charAt(0)) |
| && !Strings.isLowerCase(rightString.charAt(0))) { |
| return +1; |
| } else if (Strings.isLowerCase(rightString.charAt(0)) |
| && !Strings.isLowerCase(leftString.charAt(0))) { |
| return -1; |
| } else { |
| return leftString.compareTo(rightString); |
| } |
| } |
| |
| private int compareTypeContainerName(String leftString, |
| String rightString) { |
| int leftLength = leftString.length(); |
| int rightLength = rightString.length(); |
| if (leftLength == 0 && rightLength > 0) |
| return -1; |
| if (leftLength == 0 && rightLength == 0) |
| return 0; |
| if (leftLength > 0 && rightLength == 0) |
| return +1; |
| return compareName(leftString, rightString); |
| } |
| |
| private int compareContainerName(MethodNameMatch leftType, |
| MethodNameMatch rightType) { |
| return fLabelProvider.getContainerName(leftType) |
| .compareTo(fLabelProvider.getContainerName(rightType)); |
| } |
| |
| private int getCamelCaseCategory(MethodNameMatch type) { |
| if (fFilter == null) |
| return 0; |
| if (!fFilter.isCamelCasePattern()) |
| return 0; |
| return fFilter.matchesRawNamePattern(type) ? 0 : 1; |
| } |
| |
| private int getElementTypeCategory(MethodNameMatch type) { |
| try { |
| if (type.getProjectFragment() |
| .getKind() == IProjectFragment.K_SOURCE) |
| return 0; |
| } catch (ModelException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| return 1; |
| } |
| } |
| |
| protected class MethodInfoLabelProvider { |
| |
| private Map fLib2Name = new HashMap(); |
| private String[] fInstallLocations; |
| private String[] fVMNames; |
| |
| public MethodInfoLabelProvider() { |
| List<String> locations = new ArrayList<>(); |
| List<String> labels = new ArrayList<>(); |
| IInterpreterInstallType[] installs = ScriptRuntime |
| .getInterpreterInstallTypes( |
| fToolkit.getCoreToolkit().getNatureId()); |
| for (int i = 0; i < installs.length; i++) { |
| processVMInstallType(installs[i], locations, labels); |
| } |
| fInstallLocations = locations.toArray(new String[locations.size()]); |
| fVMNames = labels.toArray(new String[labels.size()]); |
| |
| } |
| |
| private void processVMInstallType(IInterpreterInstallType installType, |
| List locations, List labels) { |
| if (installType != null) { |
| IInterpreterInstall[] installs = installType |
| .getInterpreterInstalls(); |
| final boolean isMac = Platform.OS_MACOSX |
| .equals(Platform.getOS()); |
| for (int i = 0; i < installs.length; i++) { |
| final IInterpreterInstall install = installs[i]; |
| final String label = getFormattedLabel(install.getName()); |
| final LibraryLocation[] libLocations = install |
| .getLibraryLocations(); |
| if (libLocations != null) { |
| processLibraryLocation(libLocations, label); |
| } else { |
| String filePath = install.getInstallLocation() |
| .toOSString(); |
| /* |
| * filePath could be null if environment is configured, |
| * but environment-specific plugins are absent. |
| */ |
| if (filePath != null) { |
| // on MacOS X install locations end in an additional |
| // "/Home" segment; remove it |
| if (isMac && filePath.endsWith(HOME_SUFFIX)) |
| filePath = filePath.substring(0, |
| filePath.length() |
| - (HOME_SUFFIX.length() - 1)); |
| locations.add(filePath); |
| labels.add(label); |
| } |
| } |
| } |
| } |
| } |
| |
| private static final String HOME_SUFFIX = "/Home"; //$NON-NLS-1$ |
| |
| private void processLibraryLocation(LibraryLocation[] libLocations, |
| String label) { |
| for (int l = 0; l < libLocations.length; l++) { |
| LibraryLocation location = libLocations[l]; |
| fLib2Name.put(location.getLibraryPath().toString(), label); |
| } |
| } |
| |
| private String getFormattedLabel(String name) { |
| return Messages.format( |
| DLTKUIMessages.TypeInfoViewer_library_name_format, name); |
| } |
| |
| public String getText(Object element) { |
| MethodNameMatch type = (MethodNameMatch) element; |
| return getTypeContainerName(type, 0); |
| } |
| |
| public String getQualifiedText(MethodNameMatch type) { |
| StringBuffer result = new StringBuffer(); |
| result.append(getTypeContainerName(type, 2)); |
| // String containerName= type.getTypeContainerName(); |
| // result.append(ScriptElementLabels.CONCAT_STRING); |
| // if (containerName.length() > 0) { |
| // result.append(containerName); |
| // } else { |
| // result.append(DLTKUIMessages.TypeInfoViewer_default_package); |
| // } |
| return result.toString(); |
| } |
| |
| public String getFullyQualifiedText(MethodNameMatch type) { |
| StringBuffer result = new StringBuffer(); |
| result.append(getTypeContainerName(type, 2)); |
| // IType dltkType = ((DLTKSearchMethodNameMatch) type).getType(); |
| // ISourceModule sourceModule = (ISourceModule) |
| // dltkType.getAncestor(IModelElement.SOURCE_MODULE); |
| // String sourceModuleName = sourceModule.getElementName(); |
| // if (sourceModuleName.length() > 0) { |
| // result.append(ScriptElementLabels.CONCAT_STRING); |
| // result.append(sourceModuleName); |
| // } |
| // result.append(ScriptElementLabels.CONCAT_STRING); |
| // result.append(getContainerName(type)); |
| return result.toString(); |
| } |
| |
| public String getText(MethodNameMatch last, MethodNameMatch current, |
| MethodNameMatch next) { |
| int qualifications = 0; |
| String current0 = getTypeContainerName(current, 0); |
| String current1 = getTypeContainerName(current, 1); |
| String current2 = getTypeContainerName(current, 2); |
| if (last != null) { |
| String last0 = getTypeContainerName(last, 0); |
| String last1 = getTypeContainerName(last, 1); |
| if (current0.equals(last0)) { |
| if (current1.equals(last1)) |
| qualifications = Math.max(qualifications, 2); |
| else |
| qualifications = Math.max(qualifications, 1); |
| } |
| } |
| if (next != null) { |
| String next0 = getTypeContainerName(next, 0); |
| String next1 = getTypeContainerName(next, 1); |
| if (current0.equals(next0)) { |
| if (current1.equals(next1)) |
| qualifications = Math.max(qualifications, 2); |
| else |
| qualifications = Math.max(qualifications, 1); |
| } |
| } |
| if (qualifications > 1) { |
| return current2; |
| } |
| if (qualifications > 0) { |
| return current1; |
| } |
| return current0; |
| } |
| |
| public String getQualificationText(MethodNameMatch type) { |
| StringBuffer result = new StringBuffer(); |
| String containerName = type.getMethodContainerName(); |
| if (containerName.length() > 0) { |
| result.append(containerName); |
| result.append(ScriptElementLabels.CONCAT_STRING); |
| } |
| result.append(getContainerName(type)); |
| return result.toString(); |
| } |
| |
| // private boolean isInnerType(MethodNameMatch match) { |
| // return match.getTypeQualifiedName().indexOf('.') != -1; |
| // } |
| |
| public ImageDescriptor getImageDescriptor(Object element) { |
| MethodNameMatch method = (MethodNameMatch) element; |
| return ScriptElementImageProvider |
| .getMethodImageDescriptor(method.getModifiers()); |
| } |
| |
| private String getTypeContainerName(MethodNameMatch info, |
| int infoLevel) { |
| // String result= info.getTypeContainerName(); |
| String result = ""; //$NON-NLS-1$ |
| IDLTKUILanguageToolkit toolkit = DLTKUILanguageManager |
| .getLanguageToolkit(info.getMethod()); |
| if (toolkit != null) { |
| ScriptElementLabels labels = toolkit.getScriptElementLabels(); |
| result = labels.getElementLabel(info.getMethod(), |
| ScriptElementLabels.T_CONTAINER_QUALIFIED |
| | (infoLevel > 0 |
| ? ScriptElementLabels.APPEND_FILE |
| : 0) |
| | (infoLevel > 1 |
| ? ScriptElementLabels.CU_POST_QUALIFIED |
| : 0)); |
| } |
| if (result.length() > 0) |
| return result; |
| return DLTKUIMessages.TypeInfoViewer_default_package; |
| } |
| |
| private String getContainerName(MethodNameMatch type) { |
| IProjectFragment root = type.getProjectFragment(); |
| if (root.isExternal()) { |
| String name = root.getPath().toOSString(); |
| for (int i = 0; i < fInstallLocations.length; i++) { |
| if (name.startsWith(fInstallLocations[i])) { |
| return fVMNames[i]; |
| } |
| } |
| String lib = (String) fLib2Name.get(name); |
| if (lib != null) |
| return lib; |
| } |
| StringBuffer buf = new StringBuffer(); |
| ScriptElementLabels labels = fToolkit.getScriptElementLabels(); |
| labels.getProjectFragmentLabel(root, |
| ScriptElementLabels.ROOT_QUALIFIED |
| | ScriptElementLabels.ROOT_VARIABLE, |
| buf); |
| |
| return buf.toString(); |
| } |
| } |
| |
| private static class ProgressUpdateJob extends UIJob { |
| private MethodInfoViewer fViewer; |
| private boolean fStopped; |
| |
| public ProgressUpdateJob(Display display, MethodInfoViewer viewer) { |
| super(display, DLTKUIMessages.TypeInfoViewer_progressJob_label); |
| fViewer = viewer; |
| } |
| |
| public void stop() { |
| fStopped = true; |
| cancel(); |
| } |
| |
| @Override |
| public IStatus runInUIThread(IProgressMonitor monitor) { |
| if (stopped()) |
| return new Status(IStatus.CANCEL, DLTKUIPlugin.getPluginId(), |
| IStatus.CANCEL, "", null); //$NON-NLS-1$ |
| fViewer.updateProgressMessage(); |
| if (!stopped()) |
| schedule(300); |
| return new Status(IStatus.OK, DLTKUIPlugin.getPluginId(), |
| IStatus.OK, "", null); //$NON-NLS-1$ |
| } |
| |
| private boolean stopped() { |
| return fStopped || fViewer.getTable().isDisposed(); |
| } |
| } |
| |
| private static class ProgressMonitor extends ProgressMonitorWrapper { |
| private MethodInfoViewer fViewer; |
| private String fName; |
| private int fTotalWork; |
| private double fWorked; |
| private boolean fDone; |
| |
| public ProgressMonitor(IProgressMonitor monitor, |
| MethodInfoViewer viewer) { |
| super(monitor); |
| fViewer = viewer; |
| } |
| |
| @Override |
| public void setTaskName(String name) { |
| super.setTaskName(name); |
| fName = name; |
| } |
| |
| @Override |
| public void beginTask(String name, int totalWork) { |
| super.beginTask(name, totalWork); |
| if (fName == null) |
| fName = name; |
| fTotalWork = totalWork; |
| } |
| |
| @Override |
| public void worked(int work) { |
| super.worked(work); |
| internalWorked(work); |
| } |
| |
| @Override |
| public void done() { |
| fDone = true; |
| fViewer.setProgressMessage(""); //$NON-NLS-1$ |
| super.done(); |
| } |
| |
| @Override |
| public void internalWorked(double work) { |
| fWorked = fWorked + work; |
| fViewer.setProgressMessage(getMessage()); |
| } |
| |
| private String getMessage() { |
| if (fDone) { |
| return ""; //$NON-NLS-1$ |
| } else if (fTotalWork == 0) { |
| return fName; |
| } else { |
| return Messages.format( |
| DLTKUIMessages.TypeInfoViewer_progress_label, |
| new Object[] { fName, Integer.valueOf( |
| (int) ((fWorked * 100) / fTotalWork)) }); |
| } |
| } |
| } |
| |
| private static abstract class AbstractJob extends Job { |
| protected MethodInfoViewer fViewer; |
| |
| protected AbstractJob(String name, MethodInfoViewer viewer) { |
| super(name); |
| fViewer = viewer; |
| setSystem(true); |
| } |
| |
| @Override |
| protected final IStatus run(IProgressMonitor parent) { |
| ProgressMonitor monitor = new ProgressMonitor(parent, fViewer); |
| try { |
| fViewer.scheduleProgressUpdateJob(); |
| return doRun(monitor); |
| } finally { |
| fViewer.stopProgressUpdateJob(); |
| } |
| } |
| |
| protected abstract IStatus doRun(ProgressMonitor monitor); |
| } |
| |
| private static abstract class AbstractSearchJob extends AbstractJob { |
| private int fMode; |
| |
| protected int fTicket; |
| protected MethodInfoLabelProvider fLabelProvider; |
| |
| protected MethodInfoFilter fFilter; |
| protected OpenMethodHistory fHistory; |
| |
| protected AbstractSearchJob(int ticket, MethodInfoViewer viewer, |
| MethodInfoFilter filter, OpenMethodHistory history, |
| int numberOfVisibleItems, int mode) { |
| super(DLTKUIMessages.TypeInfoViewer_job_label, viewer); |
| fMode = mode; |
| fTicket = ticket; |
| fViewer = viewer; |
| fLabelProvider = fViewer.getLabelProvider(); |
| fFilter = filter; |
| fHistory = history; |
| } |
| |
| public void stop() { |
| cancel(); |
| } |
| |
| @Override |
| protected IStatus doRun(ProgressMonitor monitor) { |
| try { |
| if (VIRTUAL) { |
| internalRunVirtual(monitor); |
| } else { |
| internalRun(monitor); |
| } |
| } catch (CoreException e) { |
| fViewer.searchJobFailed(fTicket, e); |
| return new Status(IStatus.ERROR, DLTKUIPlugin.getPluginId(), |
| IStatus.ERROR, DLTKUIMessages.TypeInfoViewer_job_error, |
| e); |
| } catch (InterruptedException e) { |
| return canceled(e, true); |
| } catch (OperationCanceledException e) { |
| return canceled(e, false); |
| } |
| fViewer.searchJobDone(fTicket); |
| return ok(); |
| } |
| |
| protected abstract MethodNameMatch[] getSearchResult( |
| Set matchIdsInHistory, ProgressMonitor monitor) |
| throws CoreException; |
| |
| private void internalRun(ProgressMonitor monitor) |
| throws CoreException, InterruptedException { |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| fViewer.clear(fTicket); |
| |
| // local vars to speed up rendering |
| MethodNameMatch last = null; |
| MethodNameMatch type = null; |
| MethodNameMatch next = null; |
| List elements = new ArrayList(); |
| List imageDescriptors = new ArrayList(); |
| List labels = new ArrayList(); |
| Set filteredMatches = new HashSet(); |
| |
| MethodNameMatch[] matchingTypes = fHistory |
| .getFilteredTypeInfos(fFilter); |
| if (matchingTypes.length > 0) { |
| Arrays.sort(matchingTypes, |
| new MethodInfoComparator(fLabelProvider, fFilter)); |
| type = matchingTypes[0]; |
| int i = 1; |
| while (type != null) { |
| next = (i == matchingTypes.length) ? null |
| : matchingTypes[i]; |
| elements.add(type); |
| filteredMatches.add(type); |
| imageDescriptors |
| .add(fLabelProvider.getImageDescriptor(type)); |
| labels.add(fLabelProvider.getText(last, type, next)); |
| last = type; |
| type = next; |
| i++; |
| } |
| } |
| matchingTypes = null; |
| fViewer.fExpectedItemCount = elements.size(); |
| fViewer.addHistory(fTicket, elements, imageDescriptors, labels); |
| |
| if ((fMode & INDEX) == 0) { |
| return; |
| } |
| MethodNameMatch[] result = getSearchResult(filteredMatches, |
| monitor); |
| fViewer.fExpectedItemCount += result.length; |
| if (result.length == 0) { |
| return; |
| } |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| int processed = 0; |
| int nextIndex = 1; |
| type = result[0]; |
| if (!filteredMatches.isEmpty()) { |
| fViewer.addDashLineAndUpdateLastHistoryEntry(fTicket, type); |
| } |
| while (true) { |
| long startTime = System.currentTimeMillis(); |
| elements.clear(); |
| imageDescriptors.clear(); |
| labels.clear(); |
| int delta = Math.min( |
| nextIndex == 1 ? fViewer.getNumberOfVisibleItems() : 10, |
| result.length - processed); |
| if (delta == 0) |
| break; |
| processed = processed + delta; |
| while (delta > 0) { |
| next = (nextIndex == result.length) ? null |
| : result[nextIndex]; |
| elements.add(type); |
| labels.add(fLabelProvider.getText(last, type, next)); |
| imageDescriptors |
| .add(fLabelProvider.getImageDescriptor(type)); |
| last = type; |
| type = next; |
| nextIndex++; |
| delta--; |
| } |
| fViewer.addAll(fTicket, elements, imageDescriptors, labels); |
| long sleep = 100 - (System.currentTimeMillis() - startTime); |
| if (false) |
| System.out.println("Sleeping for: " + sleep); //$NON-NLS-1$ |
| |
| if (sleep > 0) |
| Thread.sleep(sleep); |
| |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| } |
| } |
| |
| private void internalRunVirtual(ProgressMonitor monitor) |
| throws CoreException, InterruptedException { |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| fViewer.clear(fTicket); |
| |
| MethodNameMatch[] matchingTypes = fHistory |
| .getFilteredTypeInfos(fFilter); |
| fViewer.setHistoryResult(fTicket, matchingTypes); |
| if ((fMode & INDEX) == 0) |
| return; |
| |
| Set filteredMatches = new HashSet(matchingTypes.length * 2); |
| for (int i = 0; i < matchingTypes.length; i++) { |
| filteredMatches.add(matchingTypes[i]); |
| } |
| |
| MethodNameMatch[] result = getSearchResult(filteredMatches, |
| monitor); |
| if (monitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| fViewer.setSearchResult(fTicket, result); |
| } |
| |
| private IStatus canceled(Exception e, boolean removePendingItems) { |
| fViewer.searchJobCanceled(fTicket, removePendingItems); |
| return new Status(IStatus.CANCEL, DLTKUIPlugin.getPluginId(), |
| IStatus.CANCEL, DLTKUIMessages.TypeInfoViewer_job_cancel, |
| e); |
| } |
| |
| private IStatus ok() { |
| return new Status(IStatus.OK, DLTKUIPlugin.getPluginId(), |
| IStatus.OK, "", null); //$NON-NLS-1$ |
| } |
| } |
| |
| private static class SearchEngineJob extends AbstractSearchJob { |
| private IDLTKSearchScope fScope; |
| private int fElementKind; |
| private SearchRequestor fReqestor; |
| |
| public SearchEngineJob(int ticket, MethodInfoViewer viewer, |
| MethodInfoFilter filter, OpenMethodHistory history, |
| int numberOfVisibleItems, int mode, IDLTKSearchScope scope, |
| int elementKind, IDLTKUILanguageToolkit toolkit) { |
| super(ticket, viewer, filter, history, numberOfVisibleItems, mode); |
| fScope = scope; |
| fElementKind = elementKind; |
| fReqestor = new SearchRequestor(filter, new MethodFilter(toolkit)); |
| } |
| |
| @Override |
| public void stop() { |
| fReqestor.cancel(); |
| super.stop(); |
| } |
| |
| @Override |
| protected MethodNameMatch[] getSearchResult(Set matchIdsInHistory, |
| ProgressMonitor monitor) throws CoreException { |
| long start = System.currentTimeMillis(); |
| fReqestor.setHistory(matchIdsInHistory); |
| |
| monitor.setTaskName( |
| DLTKUIMessages.TypeInfoViewer_searchJob_taskName); |
| |
| IMethod[] methods = new ModelAccess().findMethods( |
| fFilter.getNamePattern(), |
| ModelAccess.convertSearchRule(fFilter.getSearchFlags()), 0, |
| 0, fScope, monitor); |
| if (methods != null) { |
| for (IMethod method : methods) { |
| fReqestor.acceptMethodNameMatch( |
| new DLTKSearchMethodNameMatch(method, |
| method.getFlags())); |
| } |
| } else { |
| // consider primary working copies during searching |
| SearchEngine engine = new SearchEngine((WorkingCopyOwner) null); |
| engine.searchAllMethodNames( |
| fFilter.getNamePattern().toCharArray(), |
| fFilter.getSearchFlags(), fElementKind, fScope, |
| fReqestor, |
| IDLTKSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, |
| monitor); |
| } |
| if (DEBUG) |
| System.out.println("Time needed until search has finished: " //$NON-NLS-1$ |
| + (System.currentTimeMillis() - start)); |
| MethodNameMatch[] result = fReqestor.getResult(); |
| Arrays.sort(result, |
| new MethodInfoComparator(fLabelProvider, fFilter)); |
| if (DEBUG) |
| System.out.println("Time needed until sort has finished: " //$NON-NLS-1$ |
| + (System.currentTimeMillis() - start)); |
| fViewer.rememberResult(fTicket, result); |
| return result; |
| } |
| } |
| |
| private static class CachedResultJob extends AbstractSearchJob { |
| private MethodNameMatch[] fLastResult; |
| |
| public CachedResultJob(int ticket, MethodNameMatch[] lastResult, |
| MethodInfoViewer viewer, MethodInfoFilter filter, |
| OpenMethodHistory history, int numberOfVisibleItems, int mode) { |
| super(ticket, viewer, filter, history, numberOfVisibleItems, mode); |
| fLastResult = lastResult; |
| } |
| |
| @Override |
| protected MethodNameMatch[] getSearchResult(Set filteredHistory, |
| ProgressMonitor monitor) throws CoreException { |
| List<MethodNameMatch> result = new ArrayList<>(2048); |
| for (int i = 0; i < fLastResult.length; i++) { |
| MethodNameMatch type = fLastResult[i]; |
| if (filteredHistory.contains(type)) |
| continue; |
| if (fFilter.matchesCachedResult(type)) |
| result.add(type); |
| } |
| // we have to sort if the filter is a camel case filter. |
| MethodNameMatch[] types = result |
| .toArray(new MethodNameMatch[result.size()]); |
| if (fFilter.isCamelCasePattern()) { |
| Arrays.sort(types, |
| new MethodInfoComparator(fLabelProvider, fFilter)); |
| } |
| return types; |
| } |
| } |
| |
| private static class SyncJob extends AbstractJob { |
| private IDLTKLanguageToolkit fToolkit; |
| |
| public SyncJob(MethodInfoViewer viewer, IDLTKLanguageToolkit toolkit) { |
| super(DLTKUIMessages.TypeInfoViewer_syncJob_label, viewer); |
| this.fToolkit = toolkit; |
| } |
| |
| public void stop() { |
| cancel(); |
| } |
| |
| @Override |
| protected IStatus doRun(ProgressMonitor monitor) { |
| try { |
| monitor.setTaskName( |
| DLTKUIMessages.TypeInfoViewer_syncJob_taskName); |
| new SearchEngine().searchAllTypeNames(null, 0, // make sure we |
| // search a |
| // concrete |
| // name. This is |
| // faster |
| // according to |
| // Kent |
| "_______________".toCharArray(), //$NON-NLS-1$ |
| SearchPattern.R_EXACT_MATCH |
| | SearchPattern.R_CASE_SENSITIVE, |
| IDLTKSearchConstants.TYPE, |
| SearchEngine.createWorkspaceScope(fToolkit), |
| new NopTypeNameRequestor(), |
| IDLTKSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, |
| monitor); |
| } catch (ModelException e) { |
| DLTKUIPlugin.log(e); |
| return new Status(IStatus.ERROR, DLTKUIPlugin.getPluginId(), |
| IStatus.ERROR, DLTKUIMessages.TypeInfoViewer_job_error, |
| e); |
| } catch (OperationCanceledException e) { |
| return new Status(IStatus.CANCEL, DLTKUIPlugin.getPluginId(), |
| IStatus.CANCEL, |
| DLTKUIMessages.TypeInfoViewer_job_cancel, e); |
| } finally { |
| fViewer.syncJobDone(); |
| } |
| return new Status(IStatus.OK, DLTKUIPlugin.getPluginId(), |
| IStatus.OK, "", null); //$NON-NLS-1$ |
| } |
| } |
| |
| private static class DashLine { |
| private int fSeparatorWidth; |
| private String fMessage; |
| private int fMessageLength; |
| |
| public String getText(int width) { |
| StringBuffer dashes = new StringBuffer(); |
| int chars = (((width - fMessageLength) / fSeparatorWidth) / 2) - 2; |
| for (int i = 0; i < chars; i++) { |
| dashes.append(SEPARATOR); |
| } |
| StringBuffer result = new StringBuffer(); |
| result.append(dashes); |
| result.append(fMessage); |
| result.append(dashes); |
| return result.toString(); |
| } |
| |
| public void initialize(GC gc) { |
| fSeparatorWidth = gc.getAdvanceWidth(SEPARATOR); |
| fMessage = " " + DLTKUIMessages.TypeInfoViewer_separator_message //$NON-NLS-1$ |
| + " "; //$NON-NLS-1$ |
| fMessageLength = gc.textExtent(fMessage).x; |
| } |
| } |
| |
| private Display fDisplay; |
| |
| private String fProgressMessage; |
| private Label fProgressLabel; |
| private int fProgressCounter; |
| private ProgressUpdateJob fProgressUpdateJob; |
| |
| private OpenMethodHistory fHistory; |
| |
| /* non virtual table */ |
| private int fNextElement; |
| private List fItems; |
| |
| /* virtual table */ |
| private MethodNameMatch[] fHistoryMatches; |
| private MethodNameMatch[] fSearchMatches; |
| |
| private int fNumberOfVisibleItems; |
| private int fExpectedItemCount; |
| private Color fDashLineColor; |
| private int fScrollbarWidth; |
| private int fTableWidthDelta; |
| private int fDashLineIndex = -1; |
| private Image fSeparatorIcon; |
| private DashLine fDashLine = new DashLine(); |
| |
| private boolean fFullyQualifySelection; |
| /* remembers the last selection to restore unqualified labels */ |
| private TableItem[] fLastSelection; |
| private String[] fLastLabels; |
| |
| private MethodInfoLabelProvider fLabelProvider; |
| private ImageManager fImageManager; |
| |
| private Table fTable; |
| |
| private SyncJob fSyncJob; |
| |
| private MethodInfoFilter fMethodInfoFilter; |
| private MethodNameMatch[] fLastCompletedResult; |
| private MethodInfoFilter fLastCompletedFilter; |
| |
| private int fSearchJobTicket; |
| protected int fElementKind; |
| protected IDLTKSearchScope fSearchScope; |
| |
| private AbstractSearchJob fSearchJob; |
| |
| private static final int HISTORY = 1; |
| private static final int INDEX = 2; |
| private static final int FULL = HISTORY | INDEX; |
| |
| private static final char SEPARATOR = '-'; |
| |
| private static final boolean DEBUG = false; |
| private static final boolean VIRTUAL = false; |
| |
| private static final MethodNameMatch[] EMTPY_TYPE_INFO_ARRAY = new MethodNameMatch[0]; |
| // only needed when in virtual table mode |
| |
| private static final MethodNameMatch DASH_LINE = SearchEngine |
| .createMethodNameMatch(null, 0); |
| |
| public MethodInfoViewer(Composite parent, int flags, Label progressLabel, |
| IDLTKSearchScope scope, int elementKind, String initialFilter, |
| IDLTKUILanguageToolkit toolkit) { |
| Assert.isNotNull(scope); |
| |
| fToolkit = toolkit; |
| fDisplay = parent.getDisplay(); |
| fProgressLabel = progressLabel; |
| fSearchScope = scope; |
| fElementKind = elementKind; |
| fFullyQualifySelection = (flags & SWT.MULTI) != 0; |
| fTable = new Table(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER |
| | SWT.FLAT | flags | (VIRTUAL ? SWT.VIRTUAL : SWT.NONE)); |
| fTable.setFont(parent.getFont()); |
| fLabelProvider = new MethodInfoLabelProvider(); |
| fItems = new ArrayList(500); |
| fTable.setHeaderVisible(false); |
| addPopupMenu(); |
| fTable.addControlListener(new ControlAdapter() { |
| @Override |
| public void controlResized(ControlEvent event) { |
| int itemHeight = fTable.getItemHeight(); |
| Rectangle clientArea = fTable.getClientArea(); |
| fNumberOfVisibleItems = (clientArea.height / itemHeight) + 1; |
| } |
| }); |
| fTable.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (e.keyCode == SWT.DEL) { |
| deleteHistoryEntry(); |
| } else if (e.keyCode == SWT.ARROW_DOWN) { |
| int index = fTable.getSelectionIndex(); |
| if (index == fDashLineIndex - 1) { |
| e.doit = false; |
| setTableSelection(index + 2); |
| } |
| } else if (e.keyCode == SWT.ARROW_UP) { |
| int index = fTable.getSelectionIndex(); |
| if (fDashLineIndex != -1 && index == fDashLineIndex + 1) { |
| e.doit = false; |
| setTableSelection(index - 2); |
| } |
| } |
| } |
| }); |
| fTable.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (fLastSelection != null) { |
| for (int i = 0; i < fLastSelection.length; i++) { |
| TableItem item = fLastSelection[i]; |
| // could be disposed by deleting element from |
| // type info history |
| if (!item.isDisposed()) |
| item.setText(fLastLabels[i]); |
| } |
| } |
| TableItem[] items = fTable.getSelection(); |
| fLastSelection = new TableItem[items.length]; |
| fLastLabels = new String[items.length]; |
| for (int i = 0; i < items.length; i++) { |
| TableItem item = items[i]; |
| fLastSelection[i] = item; |
| fLastLabels[i] = item.getText(); |
| Object data = item.getData(); |
| if (data instanceof MethodNameMatch) { |
| String qualifiedText = getQualifiedText( |
| (MethodNameMatch) data); |
| if (qualifiedText.length() > fLastLabels[i].length()) |
| item.setText(qualifiedText); |
| } |
| } |
| } |
| }); |
| fTable.addDisposeListener(e -> { |
| stop(true, true); |
| fDashLineColor.dispose(); |
| fSeparatorIcon.dispose(); |
| fImageManager.dispose(); |
| if (fProgressUpdateJob != null) { |
| fProgressUpdateJob.stop(); |
| fProgressUpdateJob = null; |
| } |
| }); |
| if (VIRTUAL) { |
| fHistoryMatches = EMTPY_TYPE_INFO_ARRAY; |
| fSearchMatches = EMTPY_TYPE_INFO_ARRAY; |
| fTable.addListener(SWT.SetData, event -> { |
| TableItem item = (TableItem) event.item; |
| setData(item); |
| }); |
| } |
| |
| fDashLineColor = computeDashLineColor(); |
| fScrollbarWidth = computeScrollBarWidth(); |
| fTableWidthDelta = fTable.computeTrim(0, 0, 0, 0).width |
| - fScrollbarWidth; |
| fSeparatorIcon = DLTKPluginImages.DESC_OBJS_TYPE_SEPARATOR |
| .createImage(fTable.getDisplay()); |
| // Use a new image manager since an extension can provide its own |
| // image descriptors. To avoid thread problems with SWT the registry |
| // must be created in the UI thread. |
| fImageManager = new ImageManager(); |
| |
| fHistory = OpenMethodHistory.getInstance(this.fToolkit); |
| if (initialFilter != null && initialFilter.length() > 0) |
| fMethodInfoFilter = createMethodInfoFilter(initialFilter); |
| GC gc = null; |
| try { |
| gc = new GC(fTable); |
| gc.setFont(fTable.getFont()); |
| fDashLine.initialize(gc); |
| } finally { |
| gc.dispose(); |
| } |
| // If we do have a type info filter then we are |
| // scheduling a search job in startup. So no |
| // need to sync the search indices. |
| if (fMethodInfoFilter == null) { |
| scheduleSyncJob(); |
| } |
| } |
| |
| /* package */void startup() { |
| if (fMethodInfoFilter == null) { |
| reset(); |
| } else { |
| scheduleSearchJob(FULL); |
| } |
| } |
| |
| public Table getTable() { |
| return fTable; |
| } |
| |
| /* package */MethodInfoLabelProvider getLabelProvider() { |
| return fLabelProvider; |
| } |
| |
| private int getNumberOfVisibleItems() { |
| return fNumberOfVisibleItems; |
| } |
| |
| public void setFocus() { |
| fTable.setFocus(); |
| } |
| |
| public void setQualificationStyle(boolean value) { |
| if (fFullyQualifySelection == value) |
| return; |
| fFullyQualifySelection = value; |
| if (fLastSelection != null) { |
| for (int i = 0; i < fLastSelection.length; i++) { |
| TableItem item = fLastSelection[i]; |
| Object data = item.getData(); |
| if (data instanceof MethodNameMatch) { |
| item.setText(getQualifiedText((MethodNameMatch) data)); |
| } |
| } |
| } |
| } |
| |
| public MethodNameMatch[] getSelection() { |
| TableItem[] items = fTable.getSelection(); |
| List<MethodNameMatch> result = new ArrayList<>(items.length); |
| for (int i = 0; i < items.length; i++) { |
| Object data = items[i].getData(); |
| if (data instanceof MethodNameMatch) { |
| result.add((MethodNameMatch) data); |
| } |
| } |
| return result.toArray(new MethodNameMatch[result.size()]); |
| } |
| |
| public void stop() { |
| stop(true, false); |
| } |
| |
| public void stop(boolean stopSyncJob, boolean dispose) { |
| if (fSyncJob != null && stopSyncJob) { |
| fSyncJob.stop(); |
| fSyncJob = null; |
| } |
| if (fSearchJob != null) { |
| fSearchJob.stop(); |
| fSearchJob = null; |
| } |
| } |
| |
| public void forceSearch() { |
| stop(false, false); |
| if (fMethodInfoFilter == null) { |
| reset(); |
| } else { |
| // clear last results |
| fLastCompletedFilter = null; |
| fLastCompletedResult = null; |
| scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); |
| } |
| } |
| |
| public void setSearchPattern(String text) { |
| stop(false, false); |
| if (text.length() == 0 || "*".equals(text)) { //$NON-NLS-1$ |
| fMethodInfoFilter = null; |
| reset(); |
| } else { |
| fMethodInfoFilter = createMethodInfoFilter(text); |
| scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); |
| } |
| } |
| |
| public void setSearchScope(IDLTKSearchScope scope, boolean refresh) { |
| fSearchScope = scope; |
| if (!refresh) |
| return; |
| stop(false, false); |
| fLastCompletedFilter = null; |
| fLastCompletedResult = null; |
| if (fMethodInfoFilter == null) { |
| reset(); |
| } else { |
| scheduleSearchJob(isSyncJobRunning() ? HISTORY : FULL); |
| } |
| } |
| |
| public void reset() { |
| fLastSelection = null; |
| fLastLabels = null; |
| fExpectedItemCount = 0; |
| fDashLineIndex = -1; |
| MethodInfoFilter filter = (fMethodInfoFilter != null) |
| ? fMethodInfoFilter |
| : new MethodInfoFilter("*", fSearchScope, fElementKind); //$NON-NLS-1$ |
| if (VIRTUAL) { |
| fHistoryMatches = fHistory.getFilteredTypeInfos(filter); |
| fExpectedItemCount = fHistoryMatches.length; |
| fTable.setItemCount(fHistoryMatches.length); |
| // bug under windows. |
| if (fHistoryMatches.length == 0) { |
| fTable.redraw(); |
| } |
| fTable.clear(0, fHistoryMatches.length - 1); |
| } else { |
| fNextElement = 0; |
| MethodNameMatch[] historyItems = fHistory |
| .getFilteredTypeInfos(filter); |
| if (historyItems.length == 0) { |
| shortenTable(); |
| return; |
| } |
| fExpectedItemCount = historyItems.length; |
| int lastIndex = historyItems.length - 1; |
| MethodNameMatch last = null; |
| MethodNameMatch type = historyItems[0]; |
| for (int i = 0; i < historyItems.length; i++) { |
| MethodNameMatch next = i == lastIndex ? null |
| : historyItems[i + 1]; |
| addSingleElement(type, fLabelProvider.getImageDescriptor(type), |
| fLabelProvider.getText(last, type, next)); |
| last = type; |
| type = next; |
| } |
| shortenTable(); |
| } |
| } |
| |
| protected MethodInfoFilter createMethodInfoFilter(String text) { |
| if ("**".equals(text)) //$NON-NLS-1$ |
| text = "*"; //$NON-NLS-1$ |
| return new MethodInfoFilter(text, fSearchScope, fElementKind); |
| } |
| |
| private void addPopupMenu() { |
| Menu menu = new Menu(fTable.getShell(), SWT.POP_UP); |
| fTable.setMenu(menu); |
| final MenuItem remove = new MenuItem(menu, SWT.NONE); |
| remove.setText(DLTKUIMessages.TypeInfoViewer_remove_from_history); |
| menu.addMenuListener(new MenuAdapter() { |
| @Override |
| public void menuShown(MenuEvent e) { |
| TableItem[] selection = fTable.getSelection(); |
| remove.setEnabled(canEnable(selection)); |
| } |
| }); |
| remove.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| deleteHistoryEntry(); |
| } |
| }); |
| } |
| |
| private boolean canEnable(TableItem[] selection) { |
| if (selection.length == 0) |
| return false; |
| for (int i = 0; i < selection.length; i++) { |
| TableItem item = selection[i]; |
| Object data = item.getData(); |
| if (!(data instanceof MethodNameMatch)) |
| return false; |
| if (!(fHistory.contains((MethodNameMatch) data))) |
| return false; |
| } |
| return true; |
| } |
| |
| // ---- History management |
| // ------------------------------------------------------- |
| |
| private void deleteHistoryEntry() { |
| int index = fTable.getSelectionIndex(); |
| if (index == -1) |
| return; |
| TableItem item = fTable.getItem(index); |
| Object element = item.getData(); |
| if (!(element instanceof MethodNameMatch)) |
| return; |
| if (fHistory.remove(element) != null) { |
| item.dispose(); |
| fItems.remove(index); |
| int count = fTable.getItemCount(); |
| if (count > 0) { |
| item = fTable.getItem(0); |
| if (item.getData() instanceof DashLine) { |
| item.dispose(); |
| fItems.remove(0); |
| fDashLineIndex = -1; |
| if (count > 1) { |
| setTableSelection(0); |
| } |
| } else { |
| if (index >= count) { |
| index = count - 1; |
| } |
| setTableSelection(index); |
| } |
| } else { |
| // send dummy selection |
| fTable.notifyListeners(SWT.Selection, new Event()); |
| } |
| } |
| } |
| |
| // -- Search result updating |
| // ---------------------------------------------------- |
| |
| private void clear(int ticket) { |
| syncExec(ticket, () -> { |
| fNextElement = 0; |
| fDashLineIndex = -1; |
| fLastSelection = null; |
| fLastLabels = null; |
| fExpectedItemCount = 0; |
| }); |
| } |
| |
| private void rememberResult(int ticket, final MethodNameMatch[] result) { |
| syncExec(ticket, () -> { |
| if (fLastCompletedResult == null) { |
| fLastCompletedFilter = fMethodInfoFilter; |
| fLastCompletedResult = result; |
| } |
| }); |
| } |
| |
| private void addHistory(int ticket, final List elements, |
| final List imageDescriptors, final List labels) { |
| addAll(ticket, elements, imageDescriptors, labels); |
| } |
| |
| private void addAll(int ticket, final List elements, |
| final List imageDescriptors, final List labels) { |
| syncExec(ticket, () -> { |
| int size = elements.size(); |
| for (int i = 0; i < size; i++) { |
| addSingleElement(elements.get(i), |
| (ImageDescriptor) imageDescriptors.get(i), |
| (String) labels.get(i)); |
| } |
| }); |
| } |
| |
| private void addDashLineAndUpdateLastHistoryEntry(int ticket, |
| final MethodNameMatch next) { |
| syncExec(ticket, () -> { |
| if (fNextElement > 0) { |
| TableItem item = fTable.getItem(fNextElement - 1); |
| String label = item.getText(); |
| String newLabel = fLabelProvider.getText(null, |
| (MethodNameMatch) item.getData(), next); |
| if (fLastSelection != null && fLastSelection.length > 0 |
| && fLastSelection[fLastSelection.length - 1] == item) { |
| fLastLabels[fLastLabels.length - 1] = newLabel; |
| } else { |
| if (newLabel.length() != label.length()) |
| item.setText(newLabel); |
| } |
| } |
| fDashLineIndex = fNextElement; |
| addDashLine(); |
| }); |
| } |
| |
| private void addDashLine() { |
| TableItem item = null; |
| if (fItems.size() > fNextElement) { |
| item = (TableItem) fItems.get(fNextElement); |
| } else { |
| item = new TableItem(fTable, SWT.NONE); |
| fItems.add(item); |
| } |
| fillDashLine(item); |
| fNextElement++; |
| } |
| |
| private void addSingleElement(Object element, |
| ImageDescriptor imageDescriptor, String label) { |
| TableItem item = null; |
| Object old = null; |
| if (fItems.size() > fNextElement) { |
| item = (TableItem) fItems.get(fNextElement); |
| old = item.getData(); |
| item.setForeground(null); |
| } else { |
| item = new TableItem(fTable, SWT.NONE); |
| fItems.add(item); |
| } |
| item.setData(element); |
| item.setImage(fImageManager.get(imageDescriptor)); |
| if (fNextElement == 0) { |
| if (needsSelectionChange(old, element) || fLastSelection != null) { |
| item.setText(label); |
| fTable.setSelection(0); |
| fTable.notifyListeners(SWT.Selection, new Event()); |
| } else { |
| fLastSelection = new TableItem[] { item }; |
| fLastLabels = new String[] { label }; |
| } |
| } else { |
| item.setText(label); |
| } |
| fNextElement++; |
| } |
| |
| private boolean needsSelectionChange(Object oldElement, Object newElement) { |
| int[] selected = fTable.getSelectionIndices(); |
| if (selected.length != 1) |
| return true; |
| if (selected[0] != 0) |
| return true; |
| if (oldElement == null) |
| return true; |
| return !oldElement.equals(newElement); |
| } |
| |
| private void scheduleSearchJob(int mode) { |
| fSearchJobTicket++; |
| if (fLastCompletedFilter != null && fMethodInfoFilter |
| .isSubFilter(fLastCompletedFilter.getText())) { |
| fSearchJob = new CachedResultJob(fSearchJobTicket, |
| fLastCompletedResult, this, fMethodInfoFilter, fHistory, |
| fNumberOfVisibleItems, mode); |
| } else { |
| fLastCompletedFilter = null; |
| fLastCompletedResult = null; |
| fSearchJob = new SearchEngineJob(fSearchJobTicket, this, |
| fMethodInfoFilter, fHistory, fNumberOfVisibleItems, mode, |
| fSearchScope, fElementKind, this.fToolkit); |
| } |
| fSearchJob.schedule(); |
| } |
| |
| private void searchJobDone(int ticket) { |
| syncExec(ticket, () -> { |
| shortenTable(); |
| checkEmptyList(); |
| fSearchJob = null; |
| }); |
| } |
| |
| private void searchJobCanceled(int ticket, |
| final boolean removePendingItems) { |
| syncExec(ticket, () -> { |
| if (removePendingItems) { |
| shortenTable(); |
| checkEmptyList(); |
| } |
| fSearchJob = null; |
| }); |
| } |
| |
| private synchronized void searchJobFailed(int ticket, CoreException e) { |
| searchJobDone(ticket); |
| DLTKUIPlugin.log(e); |
| } |
| |
| // -- virtual table support |
| // ------------------------------------------------------- |
| |
| private void setHistoryResult(int ticket, final MethodNameMatch[] types) { |
| syncExec(ticket, () -> { |
| fExpectedItemCount = types.length; |
| int lastHistoryLength = fHistoryMatches.length; |
| fHistoryMatches = types; |
| int length = fHistoryMatches.length + fSearchMatches.length; |
| int dash = (fHistoryMatches.length > 0 && fSearchMatches.length > 0) |
| ? 1 |
| : 0; |
| fTable.setItemCount(length + dash); |
| if (length == 0) { |
| // bug under windows. |
| fTable.redraw(); |
| return; |
| } |
| int update = Math.max(lastHistoryLength, fHistoryMatches.length); |
| if (update > 0) { |
| fTable.clear(0, update + dash - 1); |
| } |
| }); |
| } |
| |
| private void setSearchResult(int ticket, final MethodNameMatch[] types) { |
| syncExec(ticket, () -> { |
| fExpectedItemCount += types.length; |
| fSearchMatches = types; |
| int length = fHistoryMatches.length + fSearchMatches.length; |
| int dash = (fHistoryMatches.length > 0 && fSearchMatches.length > 0) |
| ? 1 |
| : 0; |
| fTable.setItemCount(length + dash); |
| if (length == 0) { |
| // bug under windows. |
| fTable.redraw(); |
| return; |
| } |
| if (fHistoryMatches.length == 0) { |
| fTable.clear(0, length + dash - 1); |
| } else { |
| fTable.clear(fHistoryMatches.length - 1, length + dash - 1); |
| } |
| }); |
| } |
| |
| private void setData(TableItem item) { |
| int index = fTable.indexOf(item); |
| MethodNameMatch type = getTypeInfo(index); |
| if (type == DASH_LINE) { |
| item.setData(fDashLine); |
| fillDashLine(item); |
| } else { |
| item.setData(type); |
| item.setImage( |
| fImageManager.get(fLabelProvider.getImageDescriptor(type))); |
| item.setText(fLabelProvider.getText(getTypeInfo(index - 1), type, |
| getTypeInfo(index + 1))); |
| item.setForeground(null); |
| } |
| } |
| |
| private MethodNameMatch getTypeInfo(int index) { |
| if (index < 0) |
| return null; |
| if (index < fHistoryMatches.length) { |
| return fHistoryMatches[index]; |
| } |
| int dash = (fHistoryMatches.length > 0 && fSearchMatches.length > 0) ? 1 |
| : 0; |
| if (index == fHistoryMatches.length && dash == 1) { |
| return DASH_LINE; |
| } |
| index = index - fHistoryMatches.length - dash; |
| if (index >= fSearchMatches.length) |
| return null; |
| return fSearchMatches[index]; |
| } |
| |
| // -- Sync Job updates |
| // ------------------------------------------------------------ |
| |
| private void scheduleSyncJob() { |
| fSyncJob = new SyncJob(this, this.fToolkit.getCoreToolkit()); |
| fSyncJob.schedule(); |
| } |
| |
| private void syncJobDone() { |
| syncExec(() -> { |
| fSyncJob = null; |
| if (fMethodInfoFilter != null) { |
| scheduleSearchJob(FULL); |
| } |
| }); |
| } |
| |
| private boolean isSyncJobRunning() { |
| return fSyncJob != null; |
| } |
| |
| // -- progress monitor updates |
| // ----------------------------------------------------- |
| |
| private void scheduleProgressUpdateJob() { |
| syncExec(() -> { |
| if (fProgressCounter == 0) { |
| clearProgressMessage(); |
| fProgressUpdateJob = new ProgressUpdateJob(fDisplay, |
| MethodInfoViewer.this); |
| fProgressUpdateJob.schedule(300); |
| } |
| fProgressCounter++; |
| }); |
| } |
| |
| private void stopProgressUpdateJob() { |
| syncExec(() -> { |
| fProgressCounter--; |
| if (fProgressCounter == 0 && fProgressUpdateJob != null) { |
| fProgressUpdateJob.stop(); |
| fProgressUpdateJob = null; |
| clearProgressMessage(); |
| } |
| }); |
| } |
| |
| private void setProgressMessage(String message) { |
| fProgressMessage = message; |
| } |
| |
| private void clearProgressMessage() { |
| fProgressMessage = ""; //$NON-NLS-1$ |
| fProgressLabel.setText(fProgressMessage); |
| } |
| |
| private void updateProgressMessage() { |
| fProgressLabel.setText(fProgressMessage); |
| } |
| |
| // -- Helper methods |
| // -------------------------------------------------------------- |
| |
| private void syncExec(final Runnable runnable) { |
| if (fDisplay.isDisposed()) |
| return; |
| fDisplay.syncExec(() -> { |
| if (fTable.isDisposed()) |
| return; |
| runnable.run(); |
| }); |
| } |
| |
| private void syncExec(final int ticket, final Runnable runnable) { |
| if (fDisplay.isDisposed()) |
| return; |
| fDisplay.syncExec(() -> { |
| if (fTable.isDisposed() || ticket != fSearchJobTicket) |
| return; |
| runnable.run(); |
| }); |
| } |
| |
| private void fillDashLine(TableItem item) { |
| Rectangle bounds = item.getImageBounds(0); |
| Rectangle area = fTable.getBounds(); |
| boolean willHaveScrollBar = fExpectedItemCount |
| + 1 > fNumberOfVisibleItems; |
| item.setText(fDashLine |
| .getText(area.width - bounds.x - bounds.width - fTableWidthDelta |
| - (willHaveScrollBar ? fScrollbarWidth : 0))); |
| item.setImage(fSeparatorIcon); |
| item.setForeground(fDashLineColor); |
| item.setData(fDashLine); |
| } |
| |
| private void shortenTable() { |
| if (VIRTUAL) |
| return; |
| if (fNextElement < fItems.size()) { |
| fTable.setRedraw(false); |
| fTable.remove(fNextElement, fItems.size() - 1); |
| fTable.setRedraw(true); |
| } |
| for (int i = fItems.size() - 1; i >= fNextElement; i--) { |
| fItems.remove(i); |
| } |
| } |
| |
| private void checkEmptyList() { |
| if (fTable.getItemCount() == 0) { |
| fTable.notifyListeners(SWT.Selection, new Event()); |
| } |
| } |
| |
| private void setTableSelection(int index) { |
| fTable.setSelection(index); |
| fTable.notifyListeners(SWT.Selection, new Event()); |
| } |
| |
| private Color computeDashLineColor() { |
| Color fg = fTable.getForeground(); |
| int fGray = (int) (0.3 * fg.getRed() + 0.59 * fg.getGreen() |
| + 0.11 * fg.getBlue()); |
| Color bg = fTable.getBackground(); |
| int bGray = (int) (0.3 * bg.getRed() + 0.59 * bg.getGreen() |
| + 0.11 * bg.getBlue()); |
| int gray = (int) ((fGray + bGray) * 0.66); |
| return new Color(fDisplay, gray, gray, gray); |
| } |
| |
| private int computeScrollBarWidth() { |
| Composite t = new Composite(fTable.getShell(), SWT.V_SCROLL); |
| int result = t.computeTrim(0, 0, 0, 0).width; |
| t.dispose(); |
| return result; |
| } |
| |
| private String getQualifiedText(MethodNameMatch type) { |
| return fFullyQualifySelection |
| ? fLabelProvider.getFullyQualifiedText(type) |
| : fLabelProvider.getQualifiedText(type); |
| } |
| } |