package org.erlide.ui.internal.search; import java.text.Collator; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.search.ui.NewSearchUI; import org.eclipse.search.ui.text.Match; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.IWorkingSetSelectionDialog; import org.eclipse.ui.progress.IProgressService; import org.erlide.core.services.search.SearchCoreUtil; import org.erlide.engine.ErlangEngine; import org.erlide.engine.model.ErlElementKind; import org.erlide.engine.model.ErlModelException; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.IParent; import org.erlide.engine.model.erlang.IErlFunctionClause; import org.erlide.engine.model.root.IErlModule; import org.erlide.engine.services.search.ErlSearchScope; import org.erlide.engine.services.search.ErlangSearchPattern; import org.erlide.engine.services.search.FunctionPattern; import org.erlide.engine.services.search.IncludePattern; import org.erlide.engine.services.search.LimitTo; import org.erlide.engine.services.search.MacroPattern; import org.erlide.engine.services.search.ModuleLineFunctionArityRef; import org.erlide.engine.services.search.OpenResult; import org.erlide.engine.services.search.RecordFieldPattern; import org.erlide.engine.services.search.RecordPattern; import org.erlide.engine.services.search.SearchFor; import org.erlide.engine.services.search.SearchPatternFactory; import org.erlide.engine.services.search.TypeRefPattern; import org.erlide.engine.services.search.VariablePattern; import org.erlide.ui.actions.OpenUtils; import org.erlide.ui.internal.ErlideUIPlugin; import org.erlide.util.StringUtils; import org.erlide.util.Util; import org.osgi.framework.Bundle; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangRangeException; import com.ericsson.otp.erlang.OtpErlangTuple; public class SearchUtil { static final int SEARCH_IN_SOURCES = 1; static final int SEARCH_IN_EXTERNALS = 2; static final int SEARCH_IN_OTP_LIBRARIES = 4; private static final int ARI_TYPESPEC = -2; private static final int ARI_ATTRIBUTE = -3; private static final int ARI_RECORD_DEF = -4; private static final int ARI_MACRO_DEF = -5; private static final int ARI_INCLUDE = -6; private static final int ARI_RECORD_FIELD_DEF = -7; public static final class WorkingSetComparator implements Comparator<IWorkingSet> { private static Collator collator = Collator.getInstance(); @Override public int compare(final IWorkingSet o1, final IWorkingSet o2) { return collator.compare(o1.getLabel(), o2.getLabel()); } } public static ErlSearchScope getSelectionScope(final ISelection selection, final boolean addExternals, final boolean addOtp) throws CoreException { final ErlSearchScope result = new ErlSearchScope(); final Set<String> externalModulePaths = new HashSet<>(); if (selection instanceof IStructuredSelection) { final IStructuredSelection ss = (IStructuredSelection) selection; for (final Object i : ss.toList()) { if (i instanceof IResource) { final IResource r = (IResource) i; SearchCoreUtil.addResourceToScope(result, r); } else if (i instanceof IErlModule) { final IErlModule module = (IErlModule) i; result.addModule(module); } else if (i instanceof IParent) { final IParent parent = (IParent) i; SearchCoreUtil.addExternalModules(parent, result, externalModulePaths, addExternals, addOtp); } } } return result; } public static Match createMatch(final ModuleLineFunctionArityRef ref, final Map<String, IErlModule> pathToModuleMap) { final ErlangSearchElement ese = createSearchElementFromRef(ref, pathToModuleMap); return new Match(ese, ref.getOffset(), ref.getLength()); } public static ErlangSearchElement createSearchElementFromRef( final ModuleLineFunctionArityRef ref, final Map<String, IErlModule> pathToModuleMap) { final IErlModule module = pathToModuleMap.get(ref.getModulePath()); return createSearchElement(ref, module); } public static ErlangSearchElement createSearchElement( final ModuleLineFunctionArityRef ref, final IErlModule module) { return new ErlangSearchElement(module, ref.getModulePath(), ref.getName(), ref.getArity(), ref.getClauseHead(), ref.isSubClause(), refToKind(ref)); } public static ErlElementKind refToKind(final ModuleLineFunctionArityRef ref) { switch (ref.getArity()) { case ARI_TYPESPEC: return ErlElementKind.TYPESPEC; case ARI_ATTRIBUTE: return ErlElementKind.ATTRIBUTE; // Kind.MODULE; ? case ARI_RECORD_DEF: return ErlElementKind.RECORD_DEF; case ARI_MACRO_DEF: return ErlElementKind.MACRO_DEF; case ARI_INCLUDE: return ErlElementKind.ATTRIBUTE; // include actually, attributes are not saved (yet) case ARI_RECORD_FIELD_DEF: return ErlElementKind.RECORD_FIELD; default: if (ref.isSubClause()) { return ErlElementKind.CLAUSE; } return ErlElementKind.FUNCTION; } } public static ErlangSearchPattern getSearchPatternFromOpenResultAndLimitTo( final IErlModule module, final int offset, final OpenResult res, final LimitTo limitTo, final boolean matchAnyFunctionDefinition) throws ErlModelException { if (res == null) { return null; } if (res.isLocalCall()) { String moduleName; if (module != null) { moduleName = module.getModuleName(); if (offset != -1) { final IErlElement e = module.getElementAt(offset); if (new OpenUtils().isTypeDefOrRecordDef(e, res)) { return new TypeRefPattern(moduleName, res.getFun(), limitTo); } } } else { moduleName = res.getName(); } return new FunctionPattern(moduleName, res.getFun(), res.getArity(), limitTo, matchAnyFunctionDefinition, module, true); } String moduleName = res.getName(); if (moduleName == null) { return null; } final String unquoted = StringUtils.unquote(moduleName); if (res.isExternalCall()) { if (module != null && offset != -1) { final IErlElement e = module.getElementAt(offset); if (e != null && (e.getKind() == ErlElementKind.TYPESPEC || e.getKind() == ErlElementKind.RECORD_DEF)) { return new TypeRefPattern(moduleName, res.getFun(), limitTo); } } String oldName; moduleName = unquoted; do { oldName = moduleName; moduleName = ErlangEngine.getInstance().getModelFindService() .resolveMacroValue(moduleName, module); } while (!moduleName.equals(oldName)); return new FunctionPattern(moduleName, res.getFun(), res.getArity(), limitTo, matchAnyFunctionDefinition, module, false); } else if (res.isMacro()) { return new MacroPattern(unquoted, limitTo); } else if (res.isRecord()) { return new RecordPattern(unquoted, limitTo); } else if (res.isInclude()) { return new IncludePattern(moduleName, limitTo); } else if (res.isVariable()) { if (module != null) { if (offset != -1) { final IErlElement e = module.getElementAt(offset); if (e instanceof IErlFunctionClause) { final IErlFunctionClause c = (IErlFunctionClause) e; return new VariablePattern(c.getFunctionName(), c.getArity(), c.getHead(), res.getName(), limitTo, module); } } } } else if (res.isField()) { return new RecordFieldPattern(res.getFun(), unquoted, limitTo); } return null; } public static ErlangSearchPattern getSearchPattern(final IErlModule module, final SearchFor searchFor, final String pattern, final LimitTo limitTo) { String moduleName = ""; String name = pattern; int arity = -1; int p = pattern.indexOf(':'); if (p != -1) { moduleName = pattern.substring(0, p); name = pattern.substring(p + 1); } p = name.indexOf('/'); if (p != -1) { arity = Integer.valueOf(name.substring(p + 1)); name = name.substring(0, p); } return new SearchPatternFactory(ErlangEngine.getInstance().getModelUtilService()) .getSearchPattern(searchFor, moduleName, name, arity, limitTo, module); } public static void runQuery(final ErlangSearchPattern pattern, final ErlSearchScope scope, final String scopeDescription, final Shell shell) { final ErlSearchQuery query = new ErlSearchQuery(pattern, scope, scopeDescription); if (query.canRunInBackground()) { /* * This indirection with Object as parameter is needed to prevent * the loading of the Search plug-in: the VM verifies the method * call and hence loads the types used in the method signature, * eventually triggering the loading of a plug-in (in this case * ISearchQuery results in Search plug-in being loaded). */ NewSearchUI.runQueryInBackground(query); } else { final IProgressService progressService = PlatformUI.getWorkbench() .getProgressService(); /* * This indirection with Object as parameter is needed to prevent * the loading of the Search plug-in: the VM verifies the method * call and hence loads the types used in the method signature, * eventually triggering the loading of a plug-in (in this case it * would be ISearchQuery). */ final IStatus status = NewSearchUI.runQueryInForeground(progressService, query); if (status.matches(IStatus.ERROR | IStatus.INFO | IStatus.WARNING)) { ErrorDialog.openError(shell, "Search", "Problems occurred while searching. " + "The affected files will be skipped.", status); } } } public static String getWorkingSetsScopeDescription(final IWorkingSet[] workingSets) { final String wssS = "working sets "; final String wsS = "working set "; if (workingSets.length == 0) { return ""; } final String s = workingSets.length == 1 ? wsS : wssS; return workingSetLabels(workingSets, s, "'"); } private static String workingSetLabels(final IWorkingSet[] workingSets, final String s, final String surround) { final StringBuilder sb = new StringBuilder(s); int i = 0; for (final IWorkingSet ws : workingSets) { sb.append(surround).append(ws.getLabel()).append(surround).append(", "); i++; if (i == 2) { break; } } if (workingSets.length > 2) { return sb.append("... ").toString(); } return sb.substring(0, sb.length() - 2); } public static ErlSearchScope getWorkingSetsScope(final IWorkingSet[] workingSets, final boolean addExternals, final boolean addOTP) throws CoreException { final ErlSearchScope result = new ErlSearchScope(); final Set<String> externalModulePaths = new HashSet<>(); if (workingSets == null) { return result; } for (final IWorkingSet ws : workingSets) { final IAdaptable[] elements = ws.getElements(); for (final IAdaptable a : elements) { final IResource r = (IResource) a.getAdapter(IResource.class); SearchCoreUtil.addResourceToScope(result, r); IParent parent = null; Object o = a.getAdapter(IErlElement.class); if (o instanceof IParent) { parent = (IParent) o; } else { o = a.getAdapter(IResource.class); if (o != null) { final IResource resource = (IResource) o; final IErlElement element = ErlangEngine.getInstance().getModel() .findElement(resource); if (element instanceof IParent) { parent = (IParent) element; } } } if (parent != null) { SearchCoreUtil.addExternalModules(parent, result, externalModulePaths, addExternals, addOTP); } } } return result; } public static String getProjectScopeDescription(final Collection<IProject> projects) { if (projects == null || projects.isEmpty()) { return ""; } final StringBuilder sb = new StringBuilder(projects.size() == 1 ? "project" : "projects"); sb.append(' '); int i = 0; for (final IProject p : projects) { sb.append('\'').append(p.getName()).append("', "); i++; if (i == 2) { break; } } if (projects.size() > 2) { return sb.append("... ").toString(); } return sb.substring(0, sb.length() - 2); } public static String getSelectionScopeDescription(final ISelection selection) { if (selection instanceof IStructuredSelection) { final StringBuilder sb = new StringBuilder(); final IStructuredSelection structuredSelection = (IStructuredSelection) selection; int i = 0; final List<?> list = structuredSelection.toList(); if (list.isEmpty()) { return ""; } for (final Object o : list) { String name; if (o instanceof IResource) { final IResource resource = (IResource) o; name = resource.getName(); } else if (o instanceof IErlElement) { final IErlElement element = (IErlElement) o; name = element.getName(); } else { continue; } sb.append('\'').append(name).append("', "); i++; if (i == 2) { break; } } if (structuredSelection.size() > 2) { return sb.append("...").toString(); } return sb.substring(0, sb.length() - 2); } return ""; } public static String getWorkspaceScopeDescription() { return "workspace"; } public static String toString(final IWorkingSet[] workingSets) { Arrays.sort(workingSets, new WorkingSetComparator()); return workingSetLabels(workingSets, "", ""); } // LRU working sets public static final int LRU_WORKINGSET_LIST_SIZE = 3; private static LRUWorkingSetsList fgLRUWorkingSets; // Settings store private static final String DIALOG_SETTINGS_KEY = "ErlangElementSearchActions"; private static final String STORE_LRU_WORKING_SET_NAMES = "lastUsedWorkingSetNames"; // private static final String BIN_PRIM_CONST_WARN_DIALOG_ID = // "BinaryPrimitiveConstantWarningDialog"; public static boolean isSearchPlugInActivated() { return Platform.getBundle("org.eclipse.search").getState() == Bundle.ACTIVE; } private static IDialogSettings getDialogStoreSection() { final IDialogSettings dialogSettings = ErlideUIPlugin.getDefault() .getDialogSettings(); IDialogSettings settingsStore = dialogSettings.getSection(DIALOG_SETTINGS_KEY); if (settingsStore == null) { settingsStore = dialogSettings.addNewSection(DIALOG_SETTINGS_KEY); } return settingsStore; } public static LRUWorkingSetsList getLRUWorkingSets() { if (fgLRUWorkingSets == null) { restoreState(); } return fgLRUWorkingSets; } private static void restoreState() { fgLRUWorkingSets = new LRUWorkingSetsList(LRU_WORKINGSET_LIST_SIZE); final IDialogSettings settingsStore = getDialogStoreSection(); for (int i = LRU_WORKINGSET_LIST_SIZE - 1; i >= 0; i--) { final String[] lruWorkingSetNames = settingsStore .getArray(STORE_LRU_WORKING_SET_NAMES + i); if (lruWorkingSetNames != null) { final Set<IWorkingSet> workingSets = new HashSet<>(2); for (int j = 0; j < lruWorkingSetNames.length; j++) { final IWorkingSet workingSet = PlatformUI.getWorkbench() .getWorkingSetManager().getWorkingSet(lruWorkingSetNames[j]); if (workingSet != null) { workingSets.add(workingSet); } } if (!workingSets.isEmpty()) { fgLRUWorkingSets.add(workingSets.toArray(new IWorkingSet[workingSets .size()])); } } } } public static IWorkingSet[] queryWorkingSets() throws InterruptedException { final IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench() .getActiveWorkbenchWindow(); if (activeWorkbenchWindow == null) { return null; } final Shell shell = activeWorkbenchWindow.getShell(); if (shell == null) { return null; } final IWorkingSetSelectionDialog dialog = PlatformUI.getWorkbench() .getWorkingSetManager().createWorkingSetSelectionDialog(shell, true); if (dialog.open() != Window.OK) { throw new InterruptedException(); } final IWorkingSet[] workingSets = dialog.getSelection(); if (workingSets.length > 0) { return workingSets; } return null; // 'no working set' selected } public static void updateLRUWorkingSets(final IWorkingSet[] workingSets) { if (workingSets == null || workingSets.length < 1) { return; } getLRUWorkingSets().add(workingSets); saveState(getDialogStoreSection()); } private static void saveState(final IDialogSettings settingsStore) { int i = 0; for (final IWorkingSet[] workingSets : fgLRUWorkingSets.get()) { final String[] names = new String[workingSets.length]; for (int j = 0; j < workingSets.length; j++) { names[j] = workingSets[j].getName(); } settingsStore.put(STORE_LRU_WORKING_SET_NAMES + i, names); i++; } } public static void addSearchResult(final List<ModuleLineFunctionArityRef> result, final OtpErlangObject r) throws OtpErlangRangeException { final OtpErlangTuple t = (OtpErlangTuple) r; final OtpErlangList l = (OtpErlangList) t.elementAt(1); for (final OtpErlangObject i : l) { /* * find_data([#ref{function=F, arity=A, clause=C, data=D, offset=O, * length=L, sub_clause=S} | Rest], Data, M, Acc) -> case D of Data * -> find_data(Rest, Data, M, [{M, F, A, C, S, O, L} | Acc]); _ -> * find_data(Rest, Data, M, Acc) end. */ final OtpErlangTuple modLineT = (OtpErlangTuple) i; final String modName = Util.stringValue(modLineT.elementAt(0)); final OtpErlangObject nameO = modLineT.elementAt(1); final OtpErlangLong arityL = (OtpErlangLong) modLineT.elementAt(2); final int arity = arityL.intValue(); final String clauseHead = Util.stringValue(modLineT.elementAt(3)); final OtpErlangAtom subClause = (OtpErlangAtom) modLineT.elementAt(4); final OtpErlangLong offsetL = (OtpErlangLong) modLineT.elementAt(5); final OtpErlangLong lengthL = (OtpErlangLong) modLineT.elementAt(6); final OtpErlangAtom isDef = (OtpErlangAtom) modLineT.elementAt(7); String name; if (nameO instanceof OtpErlangAtom) { final OtpErlangAtom nameA = (OtpErlangAtom) nameO; name = nameA.atomValue(); } else { name = Util.stringValue(nameO); } result.add(new ModuleLineFunctionArityRef(modName, offsetL.intValue(), lengthL.intValue(), name, arity, clauseHead, Boolean .parseBoolean(subClause.atomValue()), Boolean .parseBoolean(isDef.atomValue()))); } } }