/** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license-epl.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.php.internal.search; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.FileStoreEditorInput; import org.eclipse.ui.part.FileEditorInput; import org2.eclipse.php.core.compiler.PHPFlags; import org2.eclipse.php.internal.core.PHPVersion; import org2.eclipse.php.internal.core.ast.nodes.ASTNode; import org2.eclipse.php.internal.core.ast.nodes.ClassDeclaration; import org2.eclipse.php.internal.core.ast.nodes.Expression; import org2.eclipse.php.internal.core.ast.nodes.Identifier; import org2.eclipse.php.internal.core.ast.nodes.MethodDeclaration; import org2.eclipse.php.internal.core.ast.nodes.TypeDeclaration; import com.aptana.core.logging.IdeLog; import com.aptana.editor.php.PHPEditorPlugin; import com.aptana.editor.php.core.model.IField; import com.aptana.editor.php.core.model.IMethod; import com.aptana.editor.php.core.model.IModelElement; import com.aptana.editor.php.core.model.IType; import com.aptana.editor.php.indexer.IElementEntry; import com.aptana.editor.php.indexer.IElementsIndex; import com.aptana.editor.php.indexer.IIndexChangeListener; import com.aptana.editor.php.indexer.IPHPIndexConstants; import com.aptana.editor.php.indexer.PHPGlobalIndexer; import com.aptana.editor.php.internal.builder.BuildPathManager; import com.aptana.editor.php.internal.builder.FileSystemModule; import com.aptana.editor.php.internal.builder.LocalModule; import com.aptana.editor.php.internal.contentAssist.ContentAssistFilters; import com.aptana.editor.php.internal.contentAssist.mapping.PHPOffsetMapper; import com.aptana.editor.php.internal.core.builder.IBuildPath; import com.aptana.editor.php.internal.core.builder.IModule; import com.aptana.editor.php.internal.indexer.AbstractPHPEntryValue; import com.aptana.editor.php.internal.indexer.ClassPHPEntryValue; import com.aptana.editor.php.internal.indexer.FunctionPHPEntryValue; import com.aptana.editor.php.internal.indexer.TraitPHPEntryValue; import com.aptana.editor.php.internal.indexer.VariablePHPEntryValue; import com.aptana.editor.php.internal.model.utils.ModelUtils; import com.aptana.editor.php.internal.parser.PHPParser; import com.aptana.editor.php.internal.parser.nodes.IPHPParseNode; import com.aptana.editor.php.internal.parser.nodes.PHPClassParseNode; import com.aptana.editor.php.internal.refactoring.RefactoringUtils; import com.aptana.editor.php.internal.ui.editor.PHPSourceEditor; import com.aptana.parsing.ast.IParseNode; import com.aptana.parsing.lexer.Range; /** * @author Pavel Petrochenko, Shalom Gibly */ public final class PHPSearchEngine { private static PHPSearchEngine instance; private static class ElementNode implements IElementNode { protected final IElementEntry e; protected final AbstractPHPEntryValue value; private int kind; /** * @param e * @param ea * @param kind */ private ElementNode(IElementEntry e, AbstractPHPEntryValue ea, int kind) { super(); this.e = e; this.value = ea; this.kind = kind; } /** * @see com.aptana.editor.php.search.IElementNode#toExternalReference() */ public ExternalReference toExternalReference() { IModule module = e.getModule(); Range range = new Range(value.getStartOffset(), value.getStartOffset()); return PHPSearchEngine.getInstance().getModuleReference(module, range); } /** * @see com.aptana.editor.php.search.IElementNode#getModifiers() */ public int getModifiers() { return value.getModifiers(); } /** * @see com.aptana.editor.php.search.IElementNode#getName() */ public String getName() { return e.getEntryPath(); } /** * @see com.aptana.editor.php.search.IElementNode#getPath() */ public String getPath() { IModule module = e.getModule(); if (module instanceof LocalModule) { LocalModule mz = (LocalModule) module; return mz.getFile().getFullPath().toPortableString().substring(1); } return module.getFullPath(); } public int getKind() { return kind; } } private class ClassNode extends ElementNode implements ITypeNode { protected ClassNode(IElementEntry e, ClassPHPEntryValue ea, int kind) { super(e, ea, kind); } private ClassNode(IElementEntry e, ClassPHPEntryValue ea) { this(e, ea, IElementNode.CLASS); } public boolean isOnBuildPath(IProject project) { IBuildPath buildPathByResource = BuildPathManager.getInstance().getBuildPathByResource(project); Set<IBuildPath> pa = new HashSet<IBuildPath>(); pa.add(buildPathByResource); pa.addAll(buildPathByResource.getDependencies()); if (!pa.contains(e.getModule().getBuildPath())) { return false; } return true; } public PHPClassParseNode toParseNode() { return getClassNode(e); } public String getIncludePath(IFile from) { String constructPathFromRoot = RefactoringUtils.constructPathFromRoot(e.getModule().getPath()); IPath typePath = new Path(constructPathFromRoot); IPath fullPath = from.getProjectRelativePath(); IPath commonRoot = fullPath.removeLastSegments(1); int count = 0; while (commonRoot != null && !commonRoot.isPrefixOf(typePath)) { commonRoot = commonRoot.removeLastSegments(1); count++; } StringBuilder result = new StringBuilder(); int pos = count; while (count > 0) { result.append("../"); //$NON-NLS-1$ count--; } if (pos > 0) { IPath removeFirstSegments = typePath.removeFirstSegments(commonRoot.segmentCount()); result.append(removeFirstSegments.toPortableString()); } else { result.append("./"); //$NON-NLS-1$ result.append(typePath.toPortableString()); } return result.toString(); } } private class TraitNode extends ClassNode { protected TraitNode(IElementEntry e, ClassPHPEntryValue ea) { super(e, ea, ElementNode.TRAIT); } } // private IModule getModule(EditorFileContext fileContext) // { // String struri = fileContext.getFileContext().getSourceProvider().getSourceURI(); // URI uri; // try // { // uri = new URI(struri); // } // catch (URISyntaxException e) // { // PHPEditorPlugin.logError(e); // return null; // } // IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(uri); // if (files == null || files.length == 0) // { // return null; // } // return BuildPathManager.getInstance().getModuleByResource(files[0]); // } /** * Resolves class for a given name and the current module. Does not filter results if current module can not be * determined. * * @param name * - class name. * @return parse node */ public IPHPParseNode resolveClass(String name) { IEditorPart activeEditor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage() .getActiveEditor(); if (activeEditor instanceof PHPSourceEditor) { PHPSourceEditor editor = (PHPSourceEditor) activeEditor; PHPOffsetMapper mapper = editor.getOffsetMapper(); String source = editor.getDocumentProvider().getDocument(editor.getEditorInput()).get(); IElementsIndex index = mapper.getIndex(source, source.length()); IModule emodule = editor.getModule(); List<IElementEntry> entries = index.getEntries(IElementsIndex.ANY_CETEGORY, name); Set<IElementEntry> filterByModule; if (emodule != null) { filterByModule = ContentAssistFilters.filterByModule(entries, emodule, index); } else { filterByModule = new HashSet<IElementEntry>(entries.size()); filterByModule.addAll(entries); } for (IElementEntry e : filterByModule) { if (e.getValue() instanceof ClassPHPEntryValue) { IModule module = e.getModule(); try { // FIXME: Shalom - Perhaps get the real PHP version from the module. PHPParser parser = new PHPParser(PHPVersion.getLatest(), false); IParseNode parseNode = parser.parse(module.getContents()); IParseNode findClassNode = findClassNode(parseNode, name); return (IPHPParseNode) findClassNode; } catch (Exception e1) { return null; } } } } return null; } private IParseNode findClassNode(IParseNode populateNodes, String name) { if (populateNodes instanceof IPHPParseNode) { IPHPParseNode pn = (IPHPParseNode) populateNodes; if (pn.getNodeName().equals(name) && pn instanceof PHPClassParseNode) { return pn; } IParseNode[] children = pn.getChildren(); for (IParseNode n : children) { IParseNode findClassNode = findClassNode(n, name); if (findClassNode != null) { return findClassNode; } } } return null; } /** * resolves class to reference in the code * * @param name * @return reference */ public ExternalReference resolveClassToReference(String name) { /* * IEditorPart activeEditor = PlatformUI.getWorkbench() * .getActiveWorkbenchWindow().getActivePage().getActiveEditor(); if (activeEditor instanceof PHPSourceEditor) { * PHPSourceEditor editor = (PHPSourceEditor) activeEditor; IFileLanguageService languageService = * editor.getFileContext() .getLanguageService(PHPMimeType.MIME_TYPE); PHPOffsetMapper mapper = * (PHPOffsetMapper) languageService .getOffsetMapper(); String source = editor.getViewer().getDocument().get(); * IElementsIndex index = mapper.getIndex(source, source.length()); List<IElementEntry> entries = * index.getEntries( IElementsIndex.ANY_CETEGORY, name); for (IElementEntry e : entries) { Object value = * e.getValue(); if (value instanceof ClassPHPEntryValue) { ClassPHPEntryValue ph = (ClassPHPEntryValue) value; * IModule module = e.getModule(); if (module instanceof LocalModule) { LocalModule lmodule = (LocalModule) * module; FileEditorInput fileEditorInput = new FileEditorInput( lmodule.getFile()); return new * ExternalReference(fileEditorInput, new Range(ph.getStartOffset(), ph .getEndOffset())); } if (module * instanceof FileSystemModule) { FileSystemModule ms = (FileSystemModule) module; IEditorInput * createJavaFileEditorInput = CoreUIUtils .createJavaFileEditorInput(new File(ms .getFullPath())); return new * ExternalReference(createJavaFileEditorInput, new Range(ph.getStartOffset(), ph .getEndOffset())); } } } } */ // TODO: Shalom Implement me IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Missing implementation for PHPSearchEngine::resolveClassToReference()", PHPEditorPlugin.DEBUG_SCOPE); //$NON-NLS-1$ return null; } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#getAllKnownTypes() */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Collection<ITypeNode> getAllKnownTypes() { List<IElementEntry> entriesStartingWith = PHPGlobalIndexer.getInstance().getIndex() .getEntriesStartingWith(IPHPIndexConstants.CLASS_CATEGORY, ""); //$NON-NLS-1$ List<IElementNode> nodes = new ArrayList<IElementNode>(); for (final IElementEntry e : entriesStartingWith) { processValue(nodes, e); } return (Collection) nodes; } @SuppressWarnings({ "unchecked", "rawtypes" }) private void processValue(List nodes, final IElementEntry e) { Object value = e.getValue(); if (value instanceof TraitPHPEntryValue) { TraitPHPEntryValue tv = (TraitPHPEntryValue) value; TraitNode node = new TraitNode(e, tv); nodes.add(node); } else if (value instanceof ClassPHPEntryValue) { ClassPHPEntryValue ea = (ClassPHPEntryValue) value; ITypeNode node = new ClassNode(e, ea); nodes.add(node); } else if (value instanceof FunctionPHPEntryValue) { FunctionPHPEntryValue pa = (FunctionPHPEntryValue) value; nodes.add(new ElementNode(e, pa, IElementNode.FUNCTION)); } else if (value instanceof VariablePHPEntryValue) { VariablePHPEntryValue pa = (VariablePHPEntryValue) value; nodes.add(new ElementNode(e, pa, IElementNode.CONSTANT)); } } PHPClassParseNode getClassNode(IElementEntry e) { if (e.getValue() instanceof ClassPHPEntryValue) { IModule module = e.getModule(); try { // FIXME: Shalom - Parhaps get the real PHP version from the module. PHPParser parser = new PHPParser(PHPVersion.getLatest(), false); IParseNode parseNode = parser.parse(module.getContents()); IParseNode findClassNode = findClassNode(parseNode, e.getEntryPath()); return (PHPClassParseNode) findClassNode; } catch (Exception ex) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error getting a PHP class node", ex); //$NON-NLS-1$ } } return null; } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#addListener(com.aptana.editor.php.indexer.IIndexChangeListener) */ public void addListener(IIndexChangeListener listener) { PHPGlobalIndexer.getInstance().addListener(listener); } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#removeListener(com.aptana.editor.php.indexer.IIndexChangeListener) */ public void removeListener(IIndexChangeListener listener) { PHPGlobalIndexer.getInstance().removeListener(listener); } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#getTypes(java.lang.String) */ public Collection<ITypeNode> getTypes(String name) { List<IElementEntry> entriesStartingWith = PHPGlobalIndexer.getInstance().getIndex() .getEntries(IPHPIndexConstants.CLASS_CATEGORY, name); List<ITypeNode> nodes = new ArrayList<ITypeNode>(); for (final IElementEntry e : entriesStartingWith) { processValue(nodes, e); } return nodes; } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#getAllKnownConstants() */ public Collection<IElementNode> getAllKnownConstants() { List<IElementEntry> entriesStartingWith = PHPGlobalIndexer.getInstance().getIndex() .getEntriesStartingWith(IPHPIndexConstants.CONST_CATEGORY, ""); //$NON-NLS-1$ List<IElementNode> nodes = new ArrayList<IElementNode>(); for (final IElementEntry e : entriesStartingWith) { processValue(nodes, e); } return nodes; } /** * @see com.aptana.editor.php.parsing.nodes.ITypeResolver#getAllKnownFunctions() */ public Collection<IElementNode> getAllKnownFunctions() { List<IElementEntry> entriesStartingWith = PHPGlobalIndexer.getInstance().getIndex() .getEntriesStartingWith(IPHPIndexConstants.FUNCTION_CATEGORY, ""); //$NON-NLS-1$ List<IElementNode> nodes = new ArrayList<IElementNode>(); for (final IElementEntry e : entriesStartingWith) { processValue(nodes, e); } return nodes; } /** * @see ITypeResolver#findOverriddenMethod(MethodDeclaration) */ public IMethodReference findOverriddenMethod(MethodDeclaration node) { ASTNode parent = node.getParent().getParent(); String methodname = node.getFunction().getFunctionName().getName(); if (parent instanceof ClassDeclaration) { ClassDeclaration decl = (ClassDeclaration) parent; Expression superClass = decl.getSuperClass(); if (superClass != null && superClass.getType() == ASTNode.IDENTIFIER) { String classname = ((Identifier) superClass).getName(); Set<String> visited = new HashSet<String>(); IMethodReference checkType = checkType(classname, methodname, visited); if (checkType != null) { return checkType; } } } if (parent instanceof TypeDeclaration) { TypeDeclaration decl = (TypeDeclaration) parent; for (Identifier i : decl.interfaces()) { String name = i.getName(); IMethodReference checkType = checkType(name, methodname, new HashSet<String>()); if (checkType != null) { return checkType; } } } return null; } private IMethodReference checkType(final String classname, final String methodname, Set<String> visited) { if (!visited.contains(classname)) { visited.add(classname); IElementsIndex index = PHPGlobalIndexer.getInstance().getIndex(); List<IElementEntry> entries = index.getEntries(IPHPIndexConstants.CLASS_CATEGORY, classname); for (IElementEntry e : entries) { final ClassPHPEntryValue value = (ClassPHPEntryValue) e.getValue(); List<IElementEntry> entries2 = index.getEntries(IPHPIndexConstants.FUNCTION_CATEGORY, classname + IElementsIndex.DELIMITER + methodname); if (entries2 != null && !entries2.isEmpty()) { for (final IElementEntry ea : entries2) { final FunctionPHPEntryValue mvalue = (FunctionPHPEntryValue) ea.getValue(); return new IMethodReference() { public String getQualifiedName() { return classname + "." + methodname; //$NON-NLS-1$ } public String name() { return methodname; } public ExternalReference toExternalReference() { IModule module = ea.getModule(); Range range = new Range(mvalue.getStartOffset(), mvalue.getStartOffset()); return getModuleReference(module, range); } public boolean isAbstract() { return PHPFlags.isAbstract(mvalue.getModifiers()) || PHPFlags.isInterface(value.getModifiers()); } }; } } String superClassname = value.getSuperClassname(); if (superClassname != null && superClassname.length() > 0) { IMethodReference checkType = checkType(superClassname, methodname, visited); if (checkType != null) { return checkType; } } List<String> interfaces = value.getInterfaces(); if (interfaces != null) { for (String s : interfaces) { IMethodReference checkType = checkType(s, methodname, visited); if (checkType != null) { return checkType; } } } } } return null; } /** * @param module * @param range */ protected ExternalReference getModuleReference(IModule module, Range range) { if (module instanceof LocalModule) { LocalModule lmodule = (LocalModule) module; FileEditorInput fileEditorInput = new FileEditorInput(lmodule.getFile()); return new ExternalReference(fileEditorInput, range); } if (module instanceof FileSystemModule) { try { return new ExternalReference(new FileStoreEditorInput(EFS.getStore(((FileSystemModule) module) .getExternalFile().getURI())), range); } catch (CoreException ce) { IdeLog.logError(PHPEditorPlugin.getDefault(), "Error computing the external reference", ce); //$NON-NLS-1$ } } return null; } private PHPSearchEngine() { } public static synchronized PHPSearchEngine getInstance() { if (instance == null) { instance = new PHPSearchEngine(); } return instance; } public IType[] findTypes(String name, ISearchScope scope) { List<IElementEntry> entries = PHPGlobalIndexer.getInstance().getIndex() .getEntries(IPHPIndexConstants.CLASS_CATEGORY, name); if (entries != null) { List<IType> convertClasses = ModelUtils.convertTypes(entries); return convertClasses.toArray(new IType[convertClasses.size()]); } return null; } public IMethod[] findMethods(String name, ISearchScope scope) { List<IElementEntry> entries = PHPGlobalIndexer.getInstance().getIndex() .getEntries(IPHPIndexConstants.FUNCTION_CATEGORY, name); if (entries != null) { List<IModelElement> convertClasses = ModelUtils.convertEntries(entries); return convertClasses.toArray(new IMethod[convertClasses.size()]); } return null; } public IField[] findVariables(String name, ISearchScope scope) { List<IElementEntry> entries = PHPGlobalIndexer.getInstance() .getIndex().getEntries(IPHPIndexConstants.VAR_CATEGORY, name); if (entries != null) { List<IModelElement> convertClasses = ModelUtils.convertEntries(entries); return convertClasses.toArray(new IField[convertClasses.size()]); } return null; } public IField[] findConstants(String name, ISearchScope scope) { List<IElementEntry> entries = PHPGlobalIndexer.getInstance().getIndex() .getEntries(IPHPIndexConstants.CONST_CATEGORY, name); if (entries != null) { List<IModelElement> convertClasses = ModelUtils.convertEntries(entries); return convertClasses.toArray(new IField[convertClasses.size()]); } return null; } }