/******************************************************************************* * Copyright (c) 2009 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 * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.filenetwork; import java.util.*; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.dltk.core.*; import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule; import org.eclipse.dltk.core.search.IDLTKSearchScope; import org.eclipse.dltk.core.search.SearchEngine; import org.eclipse.dltk.internal.core.ExternalSourceModule; import org.eclipse.dltk.internal.core.ModelManager; import org.eclipse.php.internal.core.Logger; import org.eclipse.php.internal.core.PHPLanguageToolkit; import org.eclipse.php.internal.core.filenetwork.ReferenceTree.Node; import org.eclipse.php.internal.core.language.LanguageModelInitializer; import org.eclipse.php.internal.core.model.IncludeField; import org.eclipse.php.internal.core.model.PHPModelAccess; import org.eclipse.php.internal.core.util.*; import org.eclipse.php.internal.core.util.PHPSearchEngine.IncludedFileResult; import org.eclipse.php.internal.core.util.PHPSearchEngine.IncludedPharFileResult; import org.eclipse.php.internal.core.util.PHPSearchEngine.ResourceResult; import org.eclipse.php.internal.core.util.PHPSearchEngine.Result; /** * This utility is used for resolving reference dependencies between files. * Usage examples: * <p> * I. Filter model elements that accessible from current source module: * * <pre> * ReferenceTree referenceTree = FileNetworkUtility.buildReferencedFilesTree(currentSourceModule, null); * List<IModelElement> filteredElements = new LinkedList<IModelElement&gt(); * for (IModelElement element : elements) { * if (referenceTree.find(element.getSourceModule()) { * filteredElements.add(element); * } * } * </pre> * * </p> * <p> * II. Find all files that reference current file and rebuild them * * <pre> * ReferenceTree referenceTree = FileNetworkUtility.buildReferencingFilesTree(currentSourceModule, null); * LinkedList<Node> nodesQ = new LinkedList<Node&gt(); * nodesQ.addFirst(referenceTree.getRoot()); * while (!nodesQ.isEmpty()) { * Node node = nodesQ.removeLast(); * rebuildFile (node.getFile()); * if (node.getChildren() != null) { * for (Node child : node.getChildren()) { * nodesQ.addFirst(child); * } * } * } * </pre> * * </p> */ public class FileNetworkUtility { /** * Analyzes file dependences, and builds tree of all source modules that * reference the given source module. * * @param file * Source module * @param monitor * Progress monitor * @return reference tree */ public static ReferenceTree buildReferencingFilesTree(ISourceModule file, IProgressMonitor monitor) { HashSet<ISourceModule> processedFiles = new HashSet<ISourceModule>(); processedFiles.add(file); Node root = new Node(file); internalBuildReferencingFilesTree(root, processedFiles, new HashMap<IModelElement, IField[]>(), monitor); return new ReferenceTree(root); } private static IDLTKSearchScope createSearchScope(ISourceModule file) { if (LanguageModelInitializer.isLanguageModelElement(file)) { return null; } if (file instanceof ExternalSourceModule) { try { IProjectFragment fileFragment = ((ExternalSourceModule) file).getProjectFragment(); List<IModelElement> scopeElements = new LinkedList<IModelElement>(); scopeElements.add(fileFragment); IScriptProject[] scriptProjects = ModelManager.getModelManager().getModel().getScriptProjects(); for (IScriptProject scriptProject : scriptProjects) { for (IProjectFragment fragment : scriptProject.getProjectFragments()) { if (fragment.equals(fileFragment)) { scopeElements.add(scriptProject); } } } return SearchEngine.createSearchScope(scopeElements.toArray(new IModelElement[scopeElements.size()]), IDLTKSearchScope.SOURCES, PHPLanguageToolkit.getDefault()); } catch (ModelException e) { return null; } } IScriptProject scriptProject = file.getScriptProject(); IProject[] referencingProjects = scriptProject.getProject().getReferencingProjects(); ArrayList<IScriptProject> scopeProjects = new ArrayList<IScriptProject>(); scopeProjects.add(scriptProject); for (IProject referencingProject : referencingProjects) { if (referencingProject.isAccessible()) { scopeProjects.add(DLTKCore.create(referencingProject)); } } return SearchEngine.createSearchScope( (IScriptProject[]) scopeProjects.toArray(new IScriptProject[scopeProjects.size()]), IDLTKSearchScope.SOURCES, PHPLanguageToolkit.getDefault()); } private static void internalBuildReferencingFilesTree(Node root, Set<ISourceModule> processedFiles, Map<IModelElement, IField[]> includesCache, IProgressMonitor monitor) { if (monitor != null && monitor.isCanceled()) { return; } ISourceModule file = root.getFile(); IDLTKSearchScope scope = createSearchScope(file); if (scope == null) { return; } IModelElement parentElement = (file instanceof ExternalSourceModule) ? ((ExternalSourceModule) file).getProjectFragment() : file.getScriptProject(); IField[] includes = includesCache.get(parentElement); if (includes == null) { includes = PHPModelAccess.getDefault().findIncludes(null, MatchRule.PREFIX, scope, monitor); includesCache.put(parentElement, includes); } for (IField include : includes) { String filePath = ((IncludeField) include).getFilePath(); String lastSegment = filePath; int i = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\')); if (i > 0) { lastSegment = lastSegment.substring(i + 1); } if (!lastSegment.equals(file.getElementName())) { continue; } // Candidate that includes the original source module: ISourceModule referencingFile = include.getSourceModule(); // Try to resolve include: ISourceModule testFile = findSourceModule(referencingFile, filePath); // If this is the correct include (that means that included file is // the original file): if (file.equals(testFile) && !processedFiles.contains(referencingFile)) { processedFiles.add(referencingFile); Node node = new Node(referencingFile); root.addChild(node); } } Collection<Node> children = root.getChildren(); if (children != null) { for (Node child : children) { internalBuildReferencingFilesTree(child, processedFiles, includesCache, monitor); } } } /** * Analyzes file dependences, and builds tree of all source modules, which * are referenced by the given source module. * * @param file * Source module * @param monitor * Progress monitor * @return reference tree */ public static ReferenceTree buildReferencedFilesTree(ISourceModule file, IProgressMonitor monitor) { return buildReferencedFilesTree(file, null, monitor); } /** * Analyzes file dependences, and builds tree of all source modules, which * are referenced by the given source module. * * @param file * Source module * @param cachedTrees * Cached reference trees from previous invocations * @param monitor * Progress monitor * @return reference tree */ public static ReferenceTree buildReferencedFilesTree(ISourceModule file, Map<ISourceModule, Node> cachedTrees, IProgressMonitor monitor) { HashSet<ISourceModule> processedFiles = new HashSet<ISourceModule>(); processedFiles.add(file); Node root; if (cachedTrees == null || (root = cachedTrees.get(file)) == null) { root = new Node(file); try { internalBuildReferencedFilesTree(root, processedFiles, cachedTrees, monitor); } catch (CoreException e) { Logger.logException(e); } } return new ReferenceTree(root); } private static void internalBuildReferencedFilesTree(final Node root, Set<ISourceModule> processedFiles, Map<ISourceModule, Node> cachedTrees, IProgressMonitor monitor) throws CoreException { ISourceModule sourceModule = root.getFile(); IField[] includes = PHPModelAccess.getDefault().findIncludes(null, MatchRule.PREFIX, SearchEngine.createSearchScope(sourceModule), monitor); if (includes == null) { return; } List<Node> nodesToBuild = new LinkedList<Node>(); for (IField include : includes) { String filePath = ((IncludeField) include).getFilePath(); ISourceModule testFile = findSourceModule(sourceModule, filePath); if (testFile != null && !processedFiles.contains(testFile)) { processedFiles.add(testFile); if (cachedTrees != null) { // use cached nodes from other trees: Node child = cachedTrees.get(testFile); if (child != null) { root.addChild(child); continue; } } Node child = new Node(testFile); nodesToBuild.add(child); root.addChild(child); if (cachedTrees != null) { // cache this tree node: cachedTrees.put(testFile, child); } } } for (Node child : nodesToBuild) { internalBuildReferencedFilesTree(child, processedFiles, cachedTrees, monitor); } } public static ISourceModule findSourceModule(ISourceModule from, String path) { return findSourceModule(from, path, null); } public static ISourceModule findSourceModule(ISourceModule from, String path, Set<String> exclusiveFiles) { ISourceModule sourceModule = null; IProject currentProject = from.getScriptProject().getProject(); String currentScriptDir = from.getParent().getPath().toString(); String currentWorkingDir = currentScriptDir; // currentProject.getFullPath().toString(); Result<?, ?> result = PHPSearchEngine.find(path, currentWorkingDir, currentScriptDir, currentProject, exclusiveFiles); if (result instanceof ResourceResult) { // workspace file ResourceResult resResult = (ResourceResult) result; IResource resource = resResult.getFile(); sourceModule = (ISourceModule) DLTKCore.create(resource); } else if (result instanceof IncludedFileResult) { IncludedFileResult incResult = (IncludedFileResult) result; IProjectFragment[] projectFragments = null; if (incResult.getProjectFragments() != null) { projectFragments = incResult.getProjectFragments(); } else if (incResult.getContainer() != null) { IProjectFragment fragment = from.getScriptProject() .getProjectFragment(incResult.getContainer().getPath()); if (fragment != null) { projectFragments = new IProjectFragment[] { fragment }; } } if (projectFragments != null) { String folderPath = ""; //$NON-NLS-1$ String moduleName = path; int i = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); if (i != -1) { folderPath = path.substring(0, i); moduleName = path.substring(i + 1); } for (IProjectFragment projectFragment : projectFragments) { IScriptFolder scriptFolder = projectFragment.getScriptFolder(folderPath); if (scriptFolder != null) { sourceModule = scriptFolder.getSourceModule(moduleName); if (sourceModule != null) { break; } } } } } else if (result instanceof IncludedPharFileResult) { sourceModule = ((IncludedPharFileResult) result).getFile(); } else { // XXX: add support for external files } return sourceModule; } }