/******************************************************************************* * Copyright (c) 2009, 2016 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.ui.explorer; import java.util.*; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.dltk.core.*; import org.eclipse.dltk.core.IOpenable; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.index2.search.ISearchEngine; import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule; import org.eclipse.dltk.core.index2.search.ISearchEngine.SearchFor; import org.eclipse.dltk.core.index2.search.ISearchRequestor; import org.eclipse.dltk.core.index2.search.ModelAccess; import org.eclipse.dltk.core.search.IDLTKSearchScope; import org.eclipse.dltk.core.search.SearchEngine; import org.eclipse.dltk.internal.core.*; import org.eclipse.dltk.internal.ui.StandardModelElementContentProvider; import org.eclipse.dltk.internal.ui.navigator.ProjectFragmentContainer; import org.eclipse.dltk.internal.ui.navigator.ScriptExplorerContentProvider; import org.eclipse.dltk.internal.ui.scriptview.BuildPathContainer; import org.eclipse.dltk.ui.DLTKPluginImages; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.php.core.PHPVersion; import org.eclipse.php.core.project.ProjectOptions; import org.eclipse.php.internal.core.PHPCoreConstants; import org.eclipse.php.internal.core.PHPLanguageToolkit; import org.eclipse.php.internal.core.includepath.IIncludepathListener; import org.eclipse.php.internal.core.includepath.IncludePath; import org.eclipse.php.internal.core.includepath.IncludePathManager; import org.eclipse.php.internal.core.language.LanguageModelInitializer; import org.eclipse.php.internal.core.project.PHPNature; import org.eclipse.php.internal.core.typeinference.GlobalNamespace; import org.eclipse.php.internal.core.util.OutlineFilter; import org.eclipse.php.internal.ui.Logger; import org.eclipse.php.internal.ui.PHPUIMessages; import org.eclipse.php.internal.ui.PHPUiPlugin; import org.eclipse.php.internal.ui.preferences.PreferenceConstants; import org.eclipse.php.internal.ui.util.NamespaceNode; import org.eclipse.wst.jsdt.core.*; import org.eclipse.wst.jsdt.core.ElementChangedEvent; import org.eclipse.wst.jsdt.core.IElementChangedListener; import org.eclipse.wst.jsdt.internal.ui.packageview.PackageFragmentRootContainer; import org.eclipse.wst.jsdt.ui.ProjectLibraryRoot; import org.eclipse.wst.jsdt.ui.StandardJavaScriptElementContentProvider; import org.eclipse.wst.jsdt.ui.project.JsNature; /** * * @author apeled, ncohen * */ public class PHPExplorerContentProvider extends ScriptExplorerContentProvider implements IIncludepathListener /* , IResourceChangeListener */, IElementChangedListener { StandardJavaScriptElementContentProvider jsContentProvider; private static int[] SEARCH_IN = { IModelElement.TYPE, IModelElement.METHOD, IModelElement.FIELD }; public PHPExplorerContentProvider(boolean provideMembers) { super(provideMembers); init(); } protected void init() { IncludePathManager.getInstance().registerIncludepathListener(this); setIsFlatLayout(false); jsContentProvider = new StandardJavaScriptElementContentProvider(true); } @Override public void setIsFlatLayout(final boolean state) { super.setIsFlatLayout(false); } @Override public void dispose() { super.dispose(); IncludePathManager.getInstance().unregisterIncludepathListener(this); JavaScriptCore.removeElementChangedListener(this); } private Object[] getNonPHPProjects(final IScriptModel model) throws ModelException { return model.getForeignResources(); } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof IPath) { IPath path = (IPath) parentElement; IWorkspace workspace = ResourcesPlugin.getWorkspace(); IResource iesource = workspace.getRoot().findMember(path); if (iesource instanceof IProject) { IProject project = (IProject) iesource; IScriptProject sp = DLTKCore.create(project); if (sp instanceof ScriptProject) { ScriptProject scriptProject = (ScriptProject) sp; try { return scriptProject.getAllProjectFragments(); } catch (ModelException e) { Logger.logException(e); } } } } // include path node if (parentElement instanceof IncludePath) { final Object entry = ((IncludePath) parentElement).getEntry(); if (entry instanceof IBuildpathEntry) { int entryKind = ((IBuildpathEntry) entry).getEntryKind(); if (entryKind == IBuildpathEntry.BPE_CONTAINER || entryKind == IBuildpathEntry.BPE_LIBRARY) { return getBuildPathEntryChildren(parentElement, entry); } } } // JavaScript nodes if (parentElement instanceof ProjectLibraryRoot) { return ((ProjectLibraryRoot) parentElement).getChildren(); } if (parentElement instanceof PackageFragmentRootContainer) { return getContainerPackageFragmentRoots((PackageFragmentRootContainer) parentElement, true); } if (parentElement instanceof org.eclipse.wst.jsdt.core.IJavaScriptElement) { return jsContentProvider.getChildren(parentElement); } try { // don't show local method variables: if (parentElement instanceof IMethod) { IMethod method = (IMethod) parentElement; return OutlineFilter.filterChildrenForOutline(method, method.getChildren()); } // aggregate php projects and non php projects (includes closed // ones) if (parentElement instanceof IScriptModel) { return StandardModelElementContentProvider.concatenate(getScriptProjects((IScriptModel) parentElement), getNonPHPProjects((IScriptModel) parentElement)); } // Handles SourceModule and downwards as well as // ExternalProjectFragments (i.e language model) if (parentElement instanceof ISourceModule || !(parentElement instanceof IOpenable) || parentElement instanceof ExternalProjectFragment) { if (parentElement instanceof IFolder) { IResource[] members = ((IFolder) parentElement).members(); ArrayList<Object> returnChlidren = new ArrayList<>(); for (IResource resource2 : members) { IModelElement modelElement = DLTKCore.create(resource2); if (modelElement != null && isSourceFolder(modelElement)) { returnChlidren.add(modelElement); } else { returnChlidren.add(resource2); } } return (Object[]) returnChlidren.toArray(new Object[returnChlidren.size()]); } for (ITreeContentProvider provider : TreeContentProviderRegistry.getInstance().getTreeProviders()) { Object[] providerChildren = provider.getChildren(parentElement); if (providerChildren != null) { return providerChildren; } } return super.getChildren(parentElement); } if (parentElement instanceof IOpenable) { if (parentElement instanceof ExternalScriptFolder) { return super.getChildren(parentElement); } IResource resource = ((IOpenable) parentElement).getResource(); if (resource instanceof IContainer) { // contributed by Toshihiro Izumi if (!resource.isAccessible()) { return NO_CHILDREN; } ArrayList<Object> returnChildren = new ArrayList<>(); boolean groupByNamespace = PHPUiPlugin.getDefault().getPreferenceStore() .getBoolean(PreferenceConstants.EXPLORER_GROUP_BY_NAMESPACES); if (groupByNamespace && parentElement instanceof IModelElement && isSourceFolder(DLTKCore.create(resource)) && supportsNamespaces(((IOpenable) parentElement).getScriptProject())) { if (hasGlobals((IModelElement) parentElement)) { returnChildren.add(new GlobalNamespace((IModelElement) parentElement)); } returnChildren.addAll(collectNamespaces(DLTKCore.create(resource))); IResource[] resChildren = ((IContainer) resource).members(); for (IResource resource2 : resChildren) { IModelElement modelElement = DLTKCore.create(resource2); if (modelElement == null || !isInSourceFolder(modelElement)) { returnChildren.add(resource2); } } } else { IResource[] resChildren = ((IContainer) resource).members(); for (IResource resource2 : resChildren) { IModelElement modelElement = DLTKCore.create(resource2); if (modelElement != null && isInSourceFolder(modelElement)) { returnChildren.add(modelElement); } else { returnChildren.add(resource2); } } } // Adding External libraries to the treeview : if (parentElement instanceof IScriptProject) { IScriptProject scriptProject = (IScriptProject) parentElement; IProject project = scriptProject.getProject(); boolean hasJsNature = JsNature.hasNature(project); if (hasJsNature) { ProjectLibraryRoot projectLibs = new ProjectLibraryRoot(JavaScriptCore.create(project)); returnChildren.add(projectLibs); } returnChildren.addAll(getScriptProjectContent(scriptProject)); } return returnChildren.toArray(); } } } catch (CoreException e) { Logger.logException(e); } if (parentElement instanceof ArchiveProjectFragment || parentElement instanceof ArchiveFolder) { return super.getChildren(parentElement); } return NO_CHILDREN; } protected List<Object> getScriptProjectContent(IScriptProject scriptProject) { List<Object> returnChildren = new ArrayList<>(); IProject project = scriptProject.getProject(); // Add include path node IncludePath[] includePaths = IncludePathManager.getInstance().getIncludePaths(project); try { if (project.hasNature(PHPNature.ID)) { IncludePathContainer incPathContainer = new IncludePathContainer(scriptProject, includePaths); returnChildren.add(incPathContainer); } } catch (CoreException e) { Logger.logException(e); } // Add the language library try { Object[] projectChildren = getProjectFragments(scriptProject); for (Object modelElement : projectChildren) { if (modelElement instanceof BuildPathContainer && ((BuildPathContainer) modelElement) .getBuildpathEntry().getPath().equals(LanguageModelInitializer.LANGUAGE_CONTAINER_PATH)) { returnChildren.add(modelElement); } } } catch (ModelException e) { Logger.logException(e); } // let extensions contribute explorer root elements for (ITreeContentProvider provider : TreeContentProviderRegistry.getInstance().getTreeProviders()) { Object[] providerChildren = provider.getChildren(scriptProject); if (providerChildren != null) { returnChildren.addAll(new ArrayList<>(Arrays.asList(providerChildren))); } } return returnChildren; } private boolean hasGlobals(IModelElement parent) { final boolean[] result = new boolean[1]; IDLTKSearchScope scope = SearchEngine.createSearchScope(parent, IDLTKSearchScope.SOURCES); ISearchEngine searchEngine = ModelAccess.getSearchEngine(PHPLanguageToolkit.getDefault()); ISearchRequestor requestor = new ISearchRequestor() { @Override public void match(int elementType, int flags, int offset, int length, int nameOffset, int nameLength, String elementName, String metadata, String doc, String qualifier, String parent, ISourceModule sourceModule, boolean isReference) { result[0] = true; } }; for (int type : SEARCH_IN) { searchEngine.search(type, PHPCoreConstants.GLOBAL_NAMESPACE, null, 0, 0, 1, SearchFor.DECLARATIONS, MatchRule.PREFIX, scope, requestor, null); if (result[0]) { return true; } } return result[0]; } private boolean isSourceFolder(IModelElement modelElement) { ScriptProject project = (ScriptProject) modelElement.getScriptProject(); IBuildpathEntry[] buildpath = null; try { buildpath = project.getResolvedBuildpath(); } catch (ModelException e) { } if (buildpath == null) { return false; } for (int j = 0, buildpathLength = buildpath.length; j < buildpathLength; j++) { IBuildpathEntry entry = buildpath[j]; // root path IPath path = entry.getPath(); if (path != null && path.equals(modelElement.getResource().getFullPath())) { return true; } } return false; } private boolean isInSourceFolder(IModelElement modelElement) { return modelElement.getScriptProject().isOnBuildpath(modelElement); } protected boolean supportsNamespaces(IScriptProject project) { PHPVersion version = ProjectOptions.getPHPVersion(project.getProject()); return version.isGreaterThan(PHPVersion.PHP5); } protected Object[] getAllNamespaces(final IScriptProject project) throws ModelException { return collectNamespaces(project).toArray(); } protected List<IType> collectNamespaces(IModelElement create) throws ModelException { IDLTKSearchScope scope = SearchEngine.createSearchScope(create, IDLTKSearchScope.SOURCES); final Set<String> names = new HashSet<>(); ISearchEngine searchEngine = ModelAccess.getSearchEngine(PHPLanguageToolkit.getDefault()); searchEngine.search(IModelElement.PACKAGE_DECLARATION, null, null, 0, 0, 0, SearchFor.DECLARATIONS, MatchRule.PREFIX, scope, new ISearchRequestor() { @Override public void match(int elementType, int flags, int offset, int length, int nameOffset, int nameLength, String elementName, String metadata, String doc, String qualifier, String parent, ISourceModule sourceModule, boolean isReference) { names.add(elementName); } }, null); List<IType> result = new LinkedList<>(); for (String entry : names) { result.add(new NamespaceNode(create, entry)); } return result; } private Object[] getContainerPackageFragmentRoots(PackageFragmentRootContainer container, boolean createFolder) { Object[] children = container.getChildren(); if (children == null) return new Object[0]; ArrayList<IJavaScriptElement> allChildren = new ArrayList<>(); ArrayList<Object> expanded = new ArrayList<>(); expanded.addAll(Arrays.asList(children)); if (expanded == null || expanded.size() < 1) return new Object[0]; Object next = expanded.remove(0); while (next != null) { try { if (next instanceof IPackageFragment) { expanded.addAll(Arrays.asList(((IPackageFragment) next).getChildren())); } else if (next instanceof IPackageFragmentRoot) { expanded.addAll(Arrays.asList(((IPackageFragmentRoot) next).getChildren())); } else if (next instanceof IClassFile) { List<IJavaScriptElement> newChildren = Arrays.asList(((IClassFile) next).getChildren()); allChildren.removeAll(newChildren); allChildren.addAll(newChildren); } else if (next instanceof IJavaScriptUnit) { List<IJavaScriptElement> newChildren = Arrays.asList(((IJavaScriptUnit) next).getChildren()); allChildren.removeAll(newChildren); allChildren.addAll(newChildren); } } catch (JavaScriptModelException ex) { Logger.logException(ex); } if (expanded.size() > 0) next = expanded.remove(0); else next = null; } return allChildren.toArray(); } /** * @param parentElement * @param entry * @return */ private Object[] getBuildPathEntryChildren(Object parentElement, Object entry) { IScriptProject scriptProject = DLTKCore.create(((IncludePath) parentElement).getProject()); IProjectFragment[] findProjectFragments = scriptProject.findProjectFragments((IBuildpathEntry) entry); List<Object> children = new LinkedList<>(); for (IProjectFragment projectFragment : findProjectFragments) { Object[] fragmentChildren = getChildren(projectFragment); children.addAll(Arrays.asList(fragmentChildren)); } if (!children.isEmpty()) { return children.toArray(new Object[children.size()]); } return getChildren(((BuildpathEntry) entry).getPath()); } public class IncludePathContainer extends ProjectFragmentContainer { private IncludePath[] fIncludePath; public IncludePathContainer(IScriptProject parent, IncludePath[] entries) { super(parent); fIncludePath = entries; } @Override public String getLabel() { return PHPUIMessages.IncludePathExplorerNode_label; } @Override public IAdaptable[] getChildren() { return fIncludePath; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + getOuterType().hashCode(); result = prime * result + Arrays.hashCode(fIncludePath); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; IncludePathContainer other = (IncludePathContainer) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (!Arrays.equals(fIncludePath, other.fIncludePath)) return false; return true; } private PHPExplorerContentProvider getOuterType() { return PHPExplorerContentProvider.this; } @Override public IProjectFragment[] getProjectFragments() { return null; } @Override public ImageDescriptor getImageDescriptor() { return DLTKPluginImages.DESC_OBJS_LIBRARY; } } /** * This method overrides the */ @Override public void refresh(IProject project) { Collection<Runnable> runnables = new ArrayList<>(); final ArrayList<IScriptProject> resources = new ArrayList<>(1); resources.add(DLTKCore.create(project)); postRefresh(resources, true, runnables); executeRunnables(runnables); } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { super.inputChanged(viewer, oldInput, newInput); if (oldInput == null && newInput != null) { JavaScriptCore.addElementChangedListener(this); } else if (oldInput != null && newInput == null) { JavaScriptCore.removeElementChangedListener(this); } } @Override public void elementChanged(ElementChangedEvent event) { IJavaScriptElementDelta[] affectedChildren = event.getDelta().getAffectedChildren(); final ArrayList<Runnable> runnables = new ArrayList<>(); for (int i = 0; i < affectedChildren.length; i++) { if (processDelta(affectedChildren[i], runnables)) { return; // early return, element got refreshed } } } private boolean processDelta(IJavaScriptElementDelta delta, ArrayList<Runnable> runnables) { int flags = delta.getFlags(); IJavaScriptElement element = delta.getElement(); int elementType = element.getElementType(); if (elementType != IJavaScriptElement.JAVASCRIPT_MODEL && elementType != IJavaScriptElement.JAVASCRIPT_PROJECT) { IJavaScriptProject proj = element.getJavaScriptProject(); if (proj == null || !proj.getProject().isOpen()) return false; } if (elementType == IJavaScriptElement.JAVASCRIPT_PROJECT) { // if the raw class path has changed we refresh the entire project if ((flags & IJavaScriptElementDelta.F_INCLUDEPATH_CHANGED) != 0) { final ArrayList<IScriptProject> resources = new ArrayList<>(1); IProject project = ((IJavaScriptProject) element).getProject(); resources.add(DLTKCore.create(project)); postRefresh(resources, true, runnables); try { this.executeRunnables(runnables); } catch (NullPointerException ex) { // workaround for bug 501274 } return true; } } return false; } @Override public Object getParent(Object element) { if (element instanceof IProjectFragment) { IResource resource = ((IModelElement) element).getResource(); if (resource instanceof IFolder && resource.getParent() != null) { IResource parentResource = resource.getParent(); IModelElement modelElement = DLTKCore.create(parentResource); if (modelElement != null) { return modelElement; } return parentResource; } } return super.getParent(element); } }