Package org.eclipse.php.internal.ui.outline

Source Code of org.eclipse.php.internal.ui.outline.PHPOutlineContentProvider$ImportContainerFinder

/*******************************************************************************
* 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.ui.outline;

import java.util.*;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.ModelElement;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.php.internal.core.PHPCoreConstants;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.compiler.ast.nodes.UsePart;
import org.eclipse.php.internal.core.compiler.ast.nodes.UseStatement;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
import org.eclipse.php.internal.core.preferences.CorePreferencesSupport;
import org.eclipse.php.internal.core.project.ProjectOptions;
import org.eclipse.php.internal.core.typeinference.FakeType;
import org.eclipse.php.internal.core.typeinference.UseStatementElement;
import org.eclipse.php.internal.core.util.OutlineFilter;
import org.eclipse.php.internal.ui.PHPUIMessages;
import org.eclipse.php.internal.ui.PHPUiPlugin;
import org.eclipse.php.internal.ui.editor.PHPStructuredEditor;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

public class PHPOutlineContentProvider implements ITreeContentProvider {

  // outline tree viewer
  private TreeViewer fOutlineViewer;
  private ISourceModule fSourceModule;
  private ISourceModule fInput;
  private IModelElement[] fUseStatements;
  private UseStatementsNode fUseStatementsNode;

  private ElementChangedListener fListener;
  private ProblemChangedListener fProblemListener;

  public PHPOutlineContentProvider(TreeViewer viewer) {
    super();
    fOutlineViewer = viewer;

    // fix bug 251682 - auto-expand outline view
    fOutlineViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);

    inputChanged(fOutlineViewer, null, null);
  }

  public void dispose() {
    if (fListener != null) {
      DLTKCore.removeElementChangedListener(fListener);
      fListener = null;
    }
    if (fProblemListener != null) {
      PHPUiPlugin.getWorkspace().removeResourceChangeListener(
          fProblemListener);
      fProblemListener = null;
    }
  }

  public Object[] getChildren(Object parent) {
    if (parent instanceof IParent) {
      IParent c = (IParent) parent;
      try {
        return OutlineFilter.filterChildrenForOutline(parent,
            c.getChildren());
      } catch (ModelException x) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=38341
        // don't log NotExist exceptions as this is a valid case
        // since we might have been posted and the element
        // removed in the meantime.
        if (DLTKCore.DEBUG || !x.isDoesNotExist()) {
          DLTKUIPlugin.log(x);
        }
      }
    }
    return PHPContentOutlineConfiguration.NO_CHILDREN;
  }

  private boolean isNamespaceSupported(IModelElement modelElement) {
    PHPVersion phpVersion = null;
    if (modelElement == null || modelElement.getScriptProject() == null) {
      String versionName = CorePreferencesSupport.getInstance()
          .getWorkspacePreferencesValue(
              PHPCoreConstants.PHP_OPTIONS_PHP_VERSION);
      phpVersion = PHPVersion.byAlias(versionName);
    } else {
      phpVersion = ProjectOptions.getPhpVersion(modelElement
          .getScriptProject().getProject());
    }
    return phpVersion.isGreaterThan(PHPVersion.PHP5);
  }

  public Object[] getElements(Object parent) {
    Object[] children = getChildren(parent);

    if (parent instanceof ISourceModule) {
      ISourceModule sourceModule = (ISourceModule) parent;

      if (isNamespaceSupported(sourceModule)) {
        // if namespaces are supported, add use statements node:
        Object[] newChildren = new Object[children.length + 1];
        fUseStatementsNode = new UseStatementsNode(sourceModule);
        newChildren[0] = fUseStatementsNode;
        System.arraycopy(children, 0, newChildren, 1, children.length);
        children = newChildren;
      }
    }
    return children;
  }

  public Object getParent(Object child) {
    if (child instanceof IModelElement) {
      IModelElement e = (IModelElement) child;
      return e.getParent();
    }
    return null;
  }

  public boolean hasChildren(Object parent) {

    if (parent instanceof IModelElement) {
      IModelElement me = (IModelElement) parent;
      if (me.getElementType() == IModelElement.FIELD
          || me.getElementType() == IModelElement.METHOD) {
        return false;
      }
    }
    if (parent instanceof IParent) {
      IParent c = (IParent) parent;
      try {
        IModelElement[] children = OutlineFilter
            .filter(c.getChildren());
        return (children != null && children.length > 0);
      } catch (ModelException x) {
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=38341
        // don't log NotExist exceptions as this is a valid case
        // since we might have been posted and the element
        // removed in the meantime.
        if (DLTKUIPlugin.isDebug() || !x.isDoesNotExist()) {
          DLTKUIPlugin.log(x);
        }
      }
    }
    return false;
  }

  /*
   * @see IContentProvider#inputChanged(Viewer, Object, Object)
   */
  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

    // Check that the new input is valid
    boolean isCU = (newInput instanceof ISourceModule);
    // Add a listener if input is valid and there wasn't one
    if (isCU && fListener == null) {
      fInput = (ISourceModule) newInput;
      fListener = new ElementChangedListener();
      DLTKCore.addElementChangedListener(fListener);

      fProblemListener = new ProblemChangedListener();
      PHPUiPlugin.getWorkspace().addResourceChangeListener(
          fProblemListener, IResourceChangeEvent.POST_CHANGE);
    }
    // If the new input is not valid and there is a listener - remove it
    else if (!isCU && fListener != null) {
      DLTKCore.removeElementChangedListener(fListener);
      fListener = null;

      PHPUiPlugin.getWorkspace().removeResourceChangeListener(
          fProblemListener);
      fProblemListener = null;

      fInput = null;
    }
  }

  public boolean isDeleted(Object o) {
    return false;
  }

  /**
   * The element change listener of the java outline viewer.
   *
   * @see IElementChangedListener
   */
  protected class ElementChangedListener implements IElementChangedListener {

    private int useStatementsCount;
    private int useStatementsCountNew;

    public void elementChanged(final ElementChangedEvent e) {
      final Control control = fOutlineViewer.getControl();
      if (control == null || control.isDisposed()) {
        return;
      }

      // filter event from different source module
      Set<ISourceModule> eventSources = new HashSet<ISourceModule>();
      collectSourceModules(e.getDelta(), eventSources);
      if (fInput == null || !eventSources.contains(fInput)) {
        return;
      }

      // update useStatements
      Job job = new Job(PHPUIMessages.PHPOutlineContentProvider_0) {

        @Override
        protected IStatus run(IProgressMonitor monitor) {
          fUseStatements = getUseStatements();

          IWorkbenchWindow activeWorkbenchWindow = PlatformUI
              .getWorkbench().getActiveWorkbenchWindow();
          if (activeWorkbenchWindow != null) {
            IWorkbenchPage activePage = activeWorkbenchWindow
                .getActivePage();
            if (activePage != null) {
              IEditorPart activeEditor = activePage
                  .getActiveEditor();
              if (activeEditor instanceof PHPStructuredEditor) {
                IModelElement base = ((PHPStructuredEditor) activeEditor)
                    .getModelElement();

                if (isNamespaceSupported(base)) {
                  ModuleDeclaration moduleDeclaration = SourceParserUtil
                      .getModuleDeclaration((ISourceModule) base);
                  UseStatement[] useStatements = ASTUtils
                      .getUseStatements(
                          moduleDeclaration,
                          moduleDeclaration
                              .sourceEnd());
                  useStatementsCountNew = useStatements.length;
                }
              }
            }
          }
          final IModelElementDelta delta = findElement(fSourceModule,
              e.getDelta());
          if ((delta != null || e.getType() == ElementChangedEvent.POST_CHANGE)
              && fOutlineViewer != null
              && fOutlineViewer.getControl() != null
              && !fOutlineViewer.getControl().isDisposed()) {
            Display d = control.getDisplay();
            if (d != null) {
              d.asyncExec(new Runnable() {
                public void run() {
                  refresh(delta);
                }
              });
            }
          }
          return Status.OK_STATUS;
        }
      };
      job.setPriority(Job.DECORATE);
      job.setSystem(true);
      job.schedule();
    }

    private void collectSourceModules(IModelElementDelta delta,
        Set<ISourceModule> sourceModules) {
      if (delta.getElement() instanceof ISourceModule) {
        sourceModules.add((ISourceModule) delta.getElement());
      }
      for (IModelElementDelta child : delta.getAffectedChildren()) {
        collectSourceModules(child, sourceModules);
      }
    }

    private void refresh(IModelElementDelta delta) {
      if (delta == null) {
        return;
      }
      if (fOutlineViewer.getTree() == null
          || (fOutlineViewer.getTree() != null && !fOutlineViewer
              .getTree().isDisposed())) {
        visitAndUpdate(delta);
      }
    }

    private void visitAndUpdate(IModelElementDelta delta) {
      if (delta.getElement() != null) {
        IModelElement modelElement = delta.getElement();
        switch (delta.getKind()) {
        case IModelElementDelta.ADDED:
          if (modelElement.getElementType() == IModelElement.IMPORT_CONTAINER) {
            IModelElement parent = new ImportContainerFinder()
                .findParent((IImportContainer) modelElement);
            if (parent != null) {
              fOutlineViewer.add(parent, modelElement);
            }
          } else {
            fOutlineViewer.add(modelElement.getParent(),
                modelElement);
          }
          refreshUseStatements(modelElement);
          break;
        case IModelElementDelta.CHANGED:
          fOutlineViewer.update(modelElement, null);
          break;
        case IModelElementDelta.REMOVED:
          fOutlineViewer.remove(modelElement);
          refreshUseStatements(modelElement);
          break;
        }
      }
      for (IModelElementDelta child : delta.getAffectedChildren()) {
        visitAndUpdate(child);
      }
    }

    private void refreshUseStatements(IModelElement element) {
      if (fUseStatementsNode != null
          && (element.getElementType() == IModelElement.IMPORT_CONTAINER || element
              .getElementType() == IModelElement.IMPORT_DECLARATION)) {
        fOutlineViewer.refresh(fUseStatementsNode);
      }
    }

    protected IModelElementDelta findElement(IModelElement unit,
        IModelElementDelta delta) {

      if (delta == null || unit == null) {
        return null;
      }

      IModelElement element = delta.getElement();

      if (unit.equals(element)) {
        if (isPossibleStructuralChange(delta)) {
          return delta;
        }
        return null;
      }

      if (element.getElementType() > IModelElement.SOURCE_MODULE) {
        return null;
      }

      IModelElementDelta[] children = delta.getAffectedChildren();
      if (children == null || children.length == 0) {
        return null;
      }

      for (int i = 0; i < children.length; i++) {
        IModelElementDelta d = findElement(unit, children[i]);
        if (d != null) {
          return d;
        }
      }

      return null;
    }

    private boolean isPossibleStructuralChange(IModelElementDelta cuDelta) {
      int oldValue = useStatementsCount;
      useStatementsCount = useStatementsCountNew;
      if (oldValue != useStatementsCountNew) {
        return true;
      }

      if (cuDelta.getKind() != IModelElementDelta.CHANGED) {
        return true; // add or remove
      }
      int flags = cuDelta.getFlags();
      if ((flags & IModelElementDelta.F_CHILDREN) != 0) {
        return true;
      }
      return (flags & (IModelElementDelta.F_CONTENT | IModelElementDelta.F_FINE_GRAINED)) == IModelElementDelta.F_CONTENT;
    }
  }

  private class ProblemChangedListener implements IResourceChangeListener,
      IResourceDeltaVisitor {

    private Set<Object> toUpdate = new HashSet<Object>();

    @Override
    public boolean visit(IResourceDelta delta) throws CoreException {
      IResource res = delta.getResource();
      if (res instanceof IProject
          && delta.getKind() == IResourceDelta.CHANGED) {
        IProject project = (IProject) res;
        if (!project.isAccessible()) {
          // only track open PHP projects
          return false;
        }
      }
      visitDelta(delta, res);
      return true;
    }

    private void visitDelta(IResourceDelta delta, IResource resource) {
      if (fInput == null) {
        return;
      }
      try {
        IResource inputResource = fInput.getCorrespondingResource();
        if (inputResource == null || !inputResource.equals(resource)) {
          return;
        }
      } catch (ModelException e) {
        return;
      }
      int kind = delta.getKind();
      if (kind == IResourceDelta.REMOVED || kind == IResourceDelta.ADDED
          || kind == IResourceDelta.CHANGED) {
        collectChangedElements(delta);
      }
    }

    private void collectChangedElements(IResourceDelta delta) {
      if ((delta.getFlags() & IResourceDelta.MARKERS) == 0) {
        return;
      }
      IMarkerDelta[] markerDeltas = delta.getMarkerDeltas();
      for (int i = 0; i < markerDeltas.length; i++) {
        if (markerDeltas[i].isSubtypeOf(IMarker.PROBLEM)) {
          Integer charStart = (Integer) markerDeltas[i]
              .getAttribute(IMarker.CHAR_START);
          Integer charEnd = (Integer) markerDeltas[i]
              .getAttribute(IMarker.CHAR_END);

          if (charStart != null && charEnd != null) {
            try {
              IModelElement element = fInput
                  .getElementAt(charStart);
              collectToUpdate(element);
            } catch (ModelException e) {
              PHPUiPlugin.log(e);
            }
          }
        }
      }
    }

    private void collectToUpdate(IModelElement element) {
      if (element == null) {
        return;
      }
      toUpdate.add(element);

      // update elements from virtual 'use statements' node
      if (fUseStatementsNode != null
          && element.getElementType() == IModelElement.IMPORT_DECLARATION) {
        Object[] objects = getChildren(fUseStatementsNode);
        toUpdate.addAll(Arrays.asList(objects));
      }
      element = element.getParent();
      while (element != null && !(element instanceof ISourceModule)) {
        toUpdate.add(element);
        // find parent for container if is in namespace
        if (element instanceof IImportContainer) {
          IModelElement parent = new ImportContainerFinder()
              .findParent((IImportContainer) element);
          if (parent != null) {
            toUpdate.add(parent);
          }
        }
        element = element.getParent();
      }
    }

    @Override
    public void resourceChanged(final IResourceChangeEvent event) {
      final Control control = fOutlineViewer.getControl();
      if (control == null || control.isDisposed()) {
        return;
      }

      Job job = new Job(PHPUIMessages.PHPOutlineContentProvider_0) {

        @Override
        protected IStatus run(IProgressMonitor monitor) {
          toUpdate.clear();
          try {
            IResourceDelta delta = event.getDelta();
            if (delta != null)
              delta.accept(ProblemChangedListener.this);
          } catch (CoreException e) {
            PHPUiPlugin.log(e.getStatus());
            return Status.OK_STATUS;
          }

          if (!toUpdate.isEmpty() && fOutlineViewer != null
              && fOutlineViewer.getControl() != null
              && !fOutlineViewer.getControl().isDisposed()) {
            Display d = control.getDisplay();
            if (d != null) {
              d.asyncExec(new Runnable() {
                public void run() {
                  fOutlineViewer.update(toUpdate.toArray(),
                      null);
                }
              });
            }
          }
          return Status.OK_STATUS;
        }
      };
      job.setPriority(Job.DECORATE);
      job.setSystem(true);
      job.schedule();
    }

  }

  private class ImportContainerFinder {

    public IModelElement findParent(IImportContainer importContainer) {
      Object[] elements = getElements(fInput);
      for (Object element : elements) {
        IModelElement parent = findParent(element, importContainer);
        if (parent != null) {
          return parent;
        }
      }
      return null;
    }

    private IModelElement findParent(Object object,
        IImportContainer importContainer) {
      Object[] children = getChildren(object);
      for (Object child : children) {
        if (child == importContainer && object instanceof IModelElement) {
          return (IModelElement) object;
        }
        IModelElement parent = findParent(child, importContainer);
        if (parent != null) {
          return parent;
        }
      }
      return null;
    }
  }

  public class UseStatementsNode extends FakeType {

    public UseStatementsNode(ISourceModule sourceModule) {
      super((ModelElement) sourceModule,
          PHPUIMessages.PHPOutlineContentProvider_useStatementsNode,
          0, null);
      fSourceModule = sourceModule;
    }

    public IModelElement[] getChildren() throws ModelException {
      if (fUseStatements == null)
        fUseStatements = getUseStatements();
      return fUseStatements;
    }

    public boolean hasChildren() throws ModelException {
      return getChildren().length > 0;
    }

    @Override
    public Object getElementInfo() throws ModelException {
      return null;
    }

    @Override
    public int getElementType() {
      return IModelElement.IMPORT_CONTAINER;
    }
  }

  private IModelElement[] getUseStatements() {
    // when rename a php file,we should return a empty array for the old
    // sourceModule,or execute SourceParserUtil.getModuleDeclaration()
    // will cache wrong ModuleDeclaration for the non-exist
    // sourceModule,so when we rename the php file back to its original
    // name will get the wrong ModuleDeclaration
    if (null == fSourceModule || !fSourceModule.exists()) {
      return new IModelElement[0];
    }
    ModuleDeclaration moduleDeclaration = SourceParserUtil
        .getModuleDeclaration(fSourceModule);
    if (moduleDeclaration == null)
      return new IModelElement[0];
    UseStatement[] useStatements = ASTUtils.getUseStatements(
        moduleDeclaration, moduleDeclaration.sourceEnd());
    List<UseStatementElement> elements = new LinkedList<UseStatementElement>();
    for (UseStatement useStatement : useStatements) {
      for (UsePart usePart : useStatement.getParts()) {
        elements.add(new UseStatementElement(
            (ModelElement) fSourceModule, usePart));
      }
    }
    return (UseStatementElement[]) elements
        .toArray(new UseStatementElement[elements.size()]);
  }

}
TOP

Related Classes of org.eclipse.php.internal.ui.outline.PHPOutlineContentProvider$ImportContainerFinder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.