Package org.eclipse.php.internal.core.codeassist

Source Code of org.eclipse.php.internal.core.codeassist.PHPSelectionEngine$SourceFieldComparator

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

import java.util.*;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.Modifiers;
import org.eclipse.dltk.ast.declarations.Declaration;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.codeassist.ScriptSelectionEngine;
import org.eclipse.dltk.compiler.env.IModuleSource;
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.evaluation.types.AmbiguousType;
import org.eclipse.dltk.evaluation.types.MultiTypeType;
import org.eclipse.dltk.internal.core.AbstractSourceModule;
import org.eclipse.dltk.internal.core.SourceRefElement;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.ISourceModuleContext;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.PHPCorePlugin;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
import org.eclipse.php.internal.core.documentModel.parser.PHPRegionContext;
import org.eclipse.php.internal.core.documentModel.parser.regions.IPhpScriptRegion;
import org.eclipse.php.internal.core.documentModel.parser.regions.PHPRegionTypes;
import org.eclipse.php.internal.core.documentModel.partitioner.PHPPartitionTypes;
import org.eclipse.php.internal.core.model.PerFileModelAccessCache;
import org.eclipse.php.internal.core.model.PhpModelAccess;
import org.eclipse.php.internal.core.project.ProjectOptions;
import org.eclipse.php.internal.core.typeinference.IModelAccessCache;
import org.eclipse.php.internal.core.typeinference.PHPClassType;
import org.eclipse.php.internal.core.typeinference.PHPModelUtils;
import org.eclipse.php.internal.core.typeinference.PHPTypeInferenceUtils;
import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext;
import org.eclipse.php.internal.core.typeinference.evaluators.PHPTraitType;
import org.eclipse.php.internal.core.util.text.PHPTextSequenceUtilities;
import org.eclipse.php.internal.core.util.text.TextSequence;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.*;

public class PHPSelectionEngine extends ScriptSelectionEngine {

  private static final String OPEN_BRACE = "("; //$NON-NLS-1$
  private static final String PROTECTED = "protected"; //$NON-NLS-1$
  private static final String PUBLIC = "public"; //$NON-NLS-1$
  private static final String PAAMAYIM_NEKUDOTAIM = "::"; //$NON-NLS-1$
  private static final String NS_SEPARATOR = "\\"; //$NON-NLS-1$
  private static final String CONST = "const"; //$NON-NLS-1$
  private static final String THIS = "this"; //$NON-NLS-1$
  private static final String STATIC = "static"; //$NON-NLS-1$
  private static final String PRIVATE = "private"; //$NON-NLS-1$
  private static final String VAR = "var"; //$NON-NLS-1$
  private static final String IMPLEMENTS = "implements"; //$NON-NLS-1$
  private static final String EXTENDS = "extends"; //$NON-NLS-1$
  private static final String NEW = "new"; //$NON-NLS-1$
  private static final String INTERFACE = "interface"; //$NON-NLS-1$
  private static final String CLASS = "class"; //$NON-NLS-1$
  private static final String FUNCTION = "function"; //$NON-NLS-1$
  private static final IModelElement[] EMPTY = {};
  private PHPVersion phpVersion;

  public IModelElement[] select(IModuleSource sourceUnit, int offset, int end) {

    if (!PHPCorePlugin.toolkitInitialized) {
      return EMPTY;
    }

    if (end < offset) {
      end = offset + 1;
    }

    ISourceModule sourceModule = (ISourceModule) sourceUnit
        .getModelElement();
    phpVersion = ProjectOptions.getPhpVersion(sourceModule
        .getScriptProject().getProject());

    // First, try to resolve using AST (if we have parsed it well):
    IModelAccessCache cache = new PerFileModelAccessCache(sourceModule) {
      @Override
      protected <T extends IModelElement> Collection<T> filterElements(
          ISourceModule sourceModule, Collection<T> elements,
          IProgressMonitor monitor) {
        // override/removes filtering because
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=442964
        return elements;
      }
    };
    try {
      IModelElement[] elements = internalASTResolve(sourceModule, cache,
          offset, end);
      if (elements != null) {
        Collection<IModelElement> filtered = PHPModelUtils
            .filterElements(sourceModule, Arrays.asList(elements),
                null, null);
        return (IModelElement[]) filtered
            .toArray(new IModelElement[filtered.size()]);
      }
    } catch (Exception e) {
      PHPCorePlugin.log(e);
    }

    // Use the old way by playing with document & buffer:
    IStructuredDocument document = null;
    IStructuredModel structuredModel = null;
    try {
      IFile file = (IFile) sourceUnit.getModelElement().getResource();
      if (file != null) {
        if (file.exists()) {
          structuredModel = StructuredModelManager.getModelManager()
              .getExistingModelForRead(file);
          if (structuredModel != null) {
            document = structuredModel.getStructuredDocument();
          } else {
            document = StructuredModelManager.getModelManager()
                .createStructuredDocumentFor(file);
          }
        } else {
          document = StructuredModelManager.getModelManager()
              .createNewStructuredDocumentFor(file);
          document.set(sourceUnit.getSourceContents());
        }
      }
    } catch (Exception e) {
      PHPCorePlugin.log(e);
    } finally {
      if (structuredModel != null) {
        structuredModel.releaseFromRead();
      }
    }

    if (document != null) {
      IModelElement[] elements = internalResolve(document, sourceModule,
          cache, offset, end);
      if (elements != null) {
        Collection<IModelElement> filtered = PHPModelUtils
            .filterElements(sourceModule, Arrays.asList(elements),
                cache, null);

        if (filtered.size() == 0) {
          return EMPTY;
        }
        IStructuredDocumentRegion sRegion = document
            .getRegionAtCharacterOffset(offset);
        if (sRegion != null) {
          ITextRegion tRegion = sRegion
              .getRegionAtCharacterOffset(offset);

          ITextRegionCollection container = sRegion;
          if (tRegion instanceof ITextRegionContainer) {
            container = (ITextRegionContainer) tRegion;
            tRegion = container.getRegionAtCharacterOffset(offset);
          }
          if (tRegion != null
              && tRegion.getType() == PHPRegionContext.PHP_CONTENT) {
            IPhpScriptRegion phpScriptRegion = (IPhpScriptRegion) tRegion;
            try {
              tRegion = phpScriptRegion.getPhpToken(offset
                  - container.getStartOffset()
                  - phpScriptRegion.getStart());
            } catch (BadLocationException e) {
            }
            if (tRegion != null) {
              // Determine element name:
              int elementStart = container.getStartOffset()
                  + phpScriptRegion.getStart()
                  + tRegion.getStart();
              TextSequence statement = PHPTextSequenceUtilities
                  .getStatement(
                      elementStart + tRegion.getLength(),
                      sRegion, true);
              if (statement != null) {
                int endPosition = PHPTextSequenceUtilities
                    .readBackwardSpaces(statement,
                        statement.length());
                int startPosition = PHPTextSequenceUtilities
                    .readIdentifierStartIndex(phpVersion,
                        statement, endPosition, true);
                String elementName = statement.subSequence(
                    startPosition, endPosition).toString();
                List<IModelElement> result = new LinkedList<IModelElement>();
                for (Iterator<IModelElement> iterator = filtered
                    .iterator(); iterator.hasNext();) {
                  IModelElement modelElement = (IModelElement) iterator
                      .next();
                  if (modelElement instanceof IField) {
                    String fieldName = elementName;
                    if (!fieldName.startsWith("$")) { //$NON-NLS-1$
                      fieldName = "$" + fieldName; //$NON-NLS-1$
                    }
                    if (modelElement.getElementName()
                        .equals(fieldName)
                        || modelElement
                            .getElementName()
                            .equals(elementName)) {
                      result.add(modelElement);
                    }
                  } else if (modelElement.getElementName()
                      .equals(elementName)) {
                    result.add(modelElement);
                  }
                }
                return (IModelElement[]) result
                    .toArray(new IModelElement[result
                        .size()]);
              }
            }
          }
        }
        return (IModelElement[]) filtered
            .toArray(new IModelElement[filtered.size()]);
      }
    }

    return EMPTY;
  }

  private IModelElement[] internalASTResolve(ISourceModule sourceModule,
      IModelAccessCache cache, int offset, int end) throws ModelException {
    String source;

    try {
      source = sourceModule.getSource();
      offset = PHPTextSequenceUtilities.readIdentifierStartIndex(source,
          offset, true);
    } catch (IndexOutOfBoundsException ex) {
      // ISourceModule.getSource() may throw
      // ArrayIndexOutOfBoundsException and
      // PHPTextSequenceUtilities.readIdentifierStartIndex() may throw
      // StringIndexOutOfBoundExceptio in case when main thread
      // modifies editor content in parallel to this action in background
      // thread
      return null;
    }

    end = PHPTextSequenceUtilities
        .readIdentifierEndIndex(source, end, true);

    int methodEnd = PHPTextSequenceUtilities.getMethodEndIndex(source, end);
    if (methodEnd != -1) {
      end = methodEnd;
    }

    ModuleDeclaration parsedUnit = SourceParserUtil.getModuleDeclaration(
        sourceModule, null);

    // boolean inDocBlock=false;
    if (parsedUnit instanceof PHPModuleDeclaration) {
      PHPModuleDeclaration phpModuleDeclaration = (PHPModuleDeclaration) parsedUnit;
      List<PHPDocBlock> phpBlocks = phpModuleDeclaration
          .getPhpDocBlocks();
      for (PHPDocBlock phpDocBlock : phpBlocks) {
        int realStart = phpDocBlock.sourceStart();
        int realEnd = phpDocBlock.sourceEnd();
        if (realStart <= offset && realEnd >= end) {
          // inDocBlock=true;
          PHPDocTag[] tags = phpDocBlock.getTags();
          return lookForMatchingTypes(tags, sourceModule, offset,
              end, cache);
        }
      }
    }
    ASTNode node = ASTUtils.findMinimalNode(parsedUnit, offset, end);
    if (node != null) {

      IContext context = ASTUtils.findContext(sourceModule, parsedUnit,
          node);
      if (context != null) {
        if (context instanceof IModelCacheContext) {
          ((IModelCacheContext) context).setCache(cache);
        }
        // Function call:
        if (node instanceof PHPCallExpression) {
          PHPCallExpression callExpression = (PHPCallExpression) node;
          if (callExpression.getReceiver() != null) {
            IEvaluatedType receiverType = PHPTypeInferenceUtils
                .resolveExpression(sourceModule, parsedUnit,
                    context, callExpression.getReceiver());
            // (new class1())->avc2()[1][1]->avc1()
            if ((receiverType instanceof MultiTypeType)
                && callExpression.getReceiver() instanceof PHPCallExpression) {
              PHPCallExpression receiverCallExpression = (PHPCallExpression) callExpression
                  .getReceiver();
              if (((PHPCallArgumentsList) receiverCallExpression
                  .getArgs()).getArrayDereferenceList() != null
                  && !((PHPCallArgumentsList) receiverCallExpression
                      .getArgs())
                      .getArrayDereferenceList()
                      .getChilds().isEmpty()) {
                receiverType = new AmbiguousType(
                    ((MultiTypeType) receiverType)
                        .getTypes().toArray(
                            new IEvaluatedType[0]));
              }
            }
            if (receiverType != null) {
              IModelElement[] elements = null;
              if ((receiverType instanceof PHPClassType)
                  && ((PHPClassType) receiverType).isGlobal()) {

                IDLTKSearchScope scope = SearchEngine
                    .createSearchScope(sourceModule
                        .getScriptProject());
                elements = PhpModelAccess.getDefault()
                    .findTypes(receiverType.getTypeName(),
                        MatchRule.EXACT, 0, 0, scope,
                        null);
                LinkedList<IModelElement> result = new LinkedList<IModelElement>();
                for (IModelElement element : elements) {
                  IModelElement parent = element.getParent();
                  while (parent.getParent() instanceof IType) {
                    parent = parent.getParent();
                  }
                  if ((parent instanceof IType)
                      && PHPFlags
                          .isNamespace(((IType) parent)
                              .getFlags())) {
                    // Do nothing
                  } else {
                    result.add(element);
                  }
                }
                elements = result.toArray(new IType[result
                    .size()]);
              } else {
                elements = PHPTypeInferenceUtils
                    .getModelElements(receiverType,
                        (ISourceModuleContext) context,
                        offset);
              }
              List<IModelElement> methods = new LinkedList<IModelElement>();
              if (elements != null) {
                for (IModelElement element : elements) {
                  if (element instanceof IType) {
                    IType type = (IType) element;
                    try {
                      IMethod[] method = PHPModelUtils
                          .getFirstTypeHierarchyMethod(
                              type,
                              cache.getSuperTypeHierarchy(
                                  type, null),
                              callExpression
                                  .getName(),
                              true, null);
                      methods.addAll(Arrays
                          .asList(method));
                    } catch (CoreException e) {
                      PHPCorePlugin.log(e);
                    }
                  }
                }
              }
              return methods.toArray(new IModelElement[methods
                  .size()]);
            }
          } else {
            SimpleReference callName = callExpression.getCallName();
            String methodName = callName instanceof FullyQualifiedReference ? ((FullyQualifiedReference) callName)
                .getFullyQualifiedName() : callName.getName();
            IMethod[] functions = PHPModelUtils.getFunctions(
                methodName, sourceModule, offset, cache, null);
            return functions == null ? EMPTY : functions;
          }
        }
        // Static field or constant access:
        else if (node instanceof StaticDispatch) {
          StaticDispatch dispatch = (StaticDispatch) node;
          String fieldName = null;
          if (dispatch instanceof StaticConstantAccess) {
            fieldName = ((StaticConstantAccess) dispatch)
                .getConstant().getName();
          } else if (dispatch instanceof StaticFieldAccess) {
            ASTNode field = ((StaticFieldAccess) dispatch)
                .getField();
            if (field instanceof VariableReference) {
              fieldName = ((VariableReference) field).getName();
            }
          }
          if (fieldName != null && dispatch.getDispatcher() != null) {
            IEvaluatedType dispatcherType = PHPTypeInferenceUtils
                .resolveExpression(sourceModule, parsedUnit,
                    context, dispatch.getDispatcher());
            if (dispatcherType != null) {
              IModelElement[] elements = PHPTypeInferenceUtils
                  .getModelElements(dispatcherType,
                      (ISourceModuleContext) context,
                      offset);
              List<IModelElement> fields = new LinkedList<IModelElement>();
              if (elements != null) {
                for (IModelElement element : elements) {
                  if (element instanceof IType) {
                    IType type = (IType) element;
                    try {
                      fields.addAll(Arrays.asList(PHPModelUtils.getTypeHierarchyField(
                          type,
                          cache.getSuperTypeHierarchy(
                              type,
                              new NullProgressMonitor()),
                          fieldName, true,
                          new NullProgressMonitor())));
                    } catch (Exception e) {
                      PHPCorePlugin.log(e);
                    }
                  }
                }
              }
              return fields.toArray(new IModelElement[fields
                  .size()]);
            }
          }
        }
        // Dynamic field access:
        else if (node instanceof FieldAccess) {
          FieldAccess fieldAccess = (FieldAccess) node;
          ASTNode field = fieldAccess.getField();
          String fieldName = null;
          if (field instanceof SimpleReference) {
            fieldName = ((SimpleReference) field).getName();
          }
          if (fieldName != null
              && fieldAccess.getDispatcher() != null) {
            IEvaluatedType dispatcherType = PHPTypeInferenceUtils
                .resolveExpression(sourceModule, parsedUnit,
                    context, fieldAccess.getDispatcher());
            if (dispatcherType != null) {
              IModelElement[] elements = PHPTypeInferenceUtils
                  .getModelElements(dispatcherType,
                      (ISourceModuleContext) context,
                      offset);
              List<IModelElement> fields = new LinkedList<IModelElement>();
              if (elements != null) {
                for (IModelElement element : elements) {
                  if (element instanceof IType) {
                    IType type = (IType) element;
                    try {
                      fields.addAll(Arrays
                          .asList(PHPModelUtils
                              .getTypeField(type,
                                  fieldName,
                                  true)));
                    } catch (ModelException e) {
                      PHPCorePlugin.log(e);
                    }
                  }
                }
              }
              return fields.toArray(new IModelElement[fields
                  .size()]);
            }
          }
        } else if (node instanceof NamespaceReference) {
          String name = ((NamespaceReference) node).getName();
          IType[] namespace = PHPModelUtils.getNamespaceOf(name
              + NamespaceReference.NAMESPACE_SEPARATOR,
              sourceModule, offset, cache, null);
          return namespace == null ? EMPTY : namespace;
        }
        // Class/Interface reference:
        else if (node instanceof TypeReference) {
          IEvaluatedType evaluatedType = PHPTypeInferenceUtils
              .resolveExpression(sourceModule, node);
          if (evaluatedType == null) {
            return EMPTY;
          }
          String name = evaluatedType.getTypeName();
          IType[] types = PHPModelUtils.getTypes(name, sourceModule,
              offset, cache, null,
              !(evaluatedType instanceof PHPTraitType));
          if (types == null || types.length == 0) {
            // This can be a constant or namespace in PHP 5.3:
            if (name.charAt(0) == NamespaceReference.NAMESPACE_SEPARATOR) {
              name = name.substring(1);
            }
            IDLTKSearchScope scope = SearchEngine
                .createSearchScope(sourceModule
                    .getScriptProject());
            if (evaluatedType instanceof PHPTraitType) {
              types = PhpModelAccess.getDefault().findTraits(
                  null, name, MatchRule.EXACT,
                  Modifiers.AccNameSpace, 0, scope, null);
            } else {
              types = PhpModelAccess.getDefault().findTypes(null,
                  name, MatchRule.EXACT,
                  Modifiers.AccNameSpace, 0, scope, null);
            }

            if (types == null || types.length == 0) {
              name = NamespaceReference.NAMESPACE_SEPARATOR
                  + name;
              return PHPModelUtils.getFields(name, sourceModule,
                  offset, cache, null);
            }
          }
          return types;
        }
        // 'new' statement
        else if (node instanceof ClassInstanceCreation) {
          ClassInstanceCreation newNode = (ClassInstanceCreation) node;

          Expression className = newNode.getClassName();

          if ((className instanceof SimpleReference || className instanceof FullyQualifiedReference)) {
            IEvaluatedType evaluatedType = PHPTypeInferenceUtils
                .resolveExpression(sourceModule, node);
            return getConstructorsIfAny(extractClasses(PHPModelUtils
                .getTypes(evaluatedType.getTypeName(),
                    sourceModule, offset, cache, null)));
          } else if ((className instanceof StaticFieldAccess)) {
            StaticFieldAccess staticFieldAccess = (StaticFieldAccess) className;
            if ((offset >= staticFieldAccess.getDispatcher()
                .sourceStart())
                && (offset <= staticFieldAccess.getDispatcher()
                    .sourceEnd())) {
              className = staticFieldAccess.getDispatcher();
              IEvaluatedType evaluatedType = PHPTypeInferenceUtils
                  .resolveExpression(sourceModule, className);
              return extractClasses(PHPModelUtils.getTypes(
                  evaluatedType.getTypeName(), sourceModule,
                  offset, cache, null));
            } else if ((offset >= staticFieldAccess.getField()
                .sourceStart())
                && (offset <= staticFieldAccess.getField()
                    .sourceEnd())) {
              className = staticFieldAccess.getField();

              String fieldName = null;
              ASTNode field = staticFieldAccess.getField();
              if (field instanceof VariableReference) {
                fieldName = ((VariableReference) field)
                    .getName();
              }
              if (fieldName != null
                  && staticFieldAccess.getDispatcher() != null) {
                IEvaluatedType dispatcherType = PHPTypeInferenceUtils
                    .resolveExpression(sourceModule,
                        parsedUnit, context,
                        staticFieldAccess
                            .getDispatcher());
                if (dispatcherType != null) {
                  IModelElement[] elements = PHPTypeInferenceUtils
                      .getModelElements(
                          dispatcherType,
                          (ISourceModuleContext) context,
                          offset);
                  List<IModelElement> fields = new LinkedList<IModelElement>();
                  if (elements != null) {
                    for (IModelElement element : elements) {
                      if (element instanceof IType) {
                        IType type = (IType) element;
                        try {
                          fields.addAll(Arrays
                              .asList(PHPModelUtils
                                  .getTypeField(
                                      type,
                                      fieldName,
                                      true)));
                        } catch (ModelException e) {
                          PHPCorePlugin.log(e);
                        }
                      }
                    }
                  }
                  return fields
                      .toArray(new IModelElement[fields
                          .size()]);
                }
              }
            }

          }
        }
        // Class name in declaration
        else if ((node instanceof TypeDeclaration || node instanceof MethodDeclaration)
            && ((Declaration) node).getNameStart() <= offset
            && ((Declaration) node).getNameEnd() >= offset) {

          IModelElement element = sourceModule.getElementAt(node
              .sourceStart());
          if (element != null) {
            return new IModelElement[] { element };
          }
        } else if (node instanceof SimpleReference) {

          SimpleReference reference = (SimpleReference) node;

          node = ASTUtils.findMinimalNode(parsedUnit, offset,
              node.end() + 1);
          if (node instanceof TraitAliasStatement) {
            node = ASTUtils.findMinimalNode(parsedUnit, offset,
                node.end() + 1);
            if (node instanceof TraitUseStatement) {
              TraitUseStatement statement = (TraitUseStatement) node;
              List<IModelElement> methods = new LinkedList<IModelElement>();
              for (TypeReference typeReference : statement
                  .getTraitList()) {
                IType[] types = PHPModelUtils.getTypes(
                    typeReference.getName(), sourceModule,
                    offset, cache, null, false);
                for (IType t : types) {
                  IModelElement[] children = t.getChildren();
                  for (IModelElement modelElement : children) {
                    String name = modelElement
                        .getElementName();
                    if (name.startsWith("$")) { //$NON-NLS-1$
                      name = name.substring(1);
                    }
                    if (name.equals(reference.getName())) {
                      methods.add(modelElement);
                    }
                  }
                }
              }

              return methods.toArray(new IMethod[methods.size()]);
            }

          }
        }
        /*
         * else if (node instanceof Scalar) { Scalar scalar = (Scalar)
         * node; if (PHPModelUtils.isQuotesString(scalar.getValue())) {
         *
         * IEvaluatedType evaluatedType = PHPTypeInferenceUtils
         * .resolveExpression(sourceModule, node); if (evaluatedType !=
         * null) {
         *
         * IType[] types = PHPModelUtils.getTypes(
         * evaluatedType.getTypeName(), sourceModule, offset, null,
         * null); return types; } } }
         */
      }
    }
    return null;
  }

  private IType[] lookForMatchingTypes(PHPDocTag[] tags,
      ISourceModule sourceModule, int offset, int end,
      IModelAccessCache cache) throws ModelException {
    if (tags == null) {
      return null;
    }
    for (PHPDocTag phpDocTag : tags) {
      if (phpDocTag.sourceStart() <= offset
          && phpDocTag.sourceEnd() >= end) {
        SimpleReference[] references = phpDocTag.getReferences();
        if (references != null) {
          for (SimpleReference simpleReference : references) {
            if (simpleReference instanceof TypeReference) {
              TypeReference typeReference = (TypeReference) simpleReference;
              if (typeReference.sourceStart() <= offset
                  && typeReference.sourceEnd() >= end) {
                String name = typeReference.getName();

                // remove additional end elements like '[]'
                if (typeReference.sourceEnd() > end) {
                  int startShift = offset
                      - typeReference.sourceStart();
                  name = typeReference.getName().substring(
                      startShift,
                      (end - offset) + startShift);
                }

                IType[] types = filterNS(PHPModelUtils
                    .getTypes(name, sourceModule, offset,
                        cache, null));
                return types;

              }
            }
          }
        }
      }
    }
    return null;
  }

  private IType[] filterNS(IType[] types) throws ModelException {
    if (types == null) {
      return types;
    } else {
      Set<? super IType> result = new HashSet<IType>();
      for (IType type : types) {
        if (PHPFlags.isClass(type.getFlags())
            || PHPFlags.isInterface(type.getFlags())) {
          result.add(type);
        }
      }
      return (IType[]) result.toArray(new IType[result.size()]);
    }
  }

  private IModelElement[] internalResolve(IStructuredDocument sDoc,
      ISourceModule sourceModule, IModelAccessCache cache, int offset,
      int end) {
    try {
      IStructuredDocumentRegion sRegion = sDoc
          .getRegionAtCharacterOffset(offset);
      if (sRegion != null) {
        ITextRegion tRegion = sRegion
            .getRegionAtCharacterOffset(offset);

        ITextRegionCollection container = sRegion;
        if (tRegion instanceof ITextRegionContainer) {
          container = (ITextRegionContainer) tRegion;
          tRegion = container.getRegionAtCharacterOffset(offset);
        }

        if (tRegion != null
            && tRegion.getType() == PHPRegionContext.PHP_CONTENT) {
          IPhpScriptRegion phpScriptRegion = (IPhpScriptRegion) tRegion;
          tRegion = phpScriptRegion.getPhpToken(offset
              - container.getStartOffset()
              - phpScriptRegion.getStart());
          if (tRegion == null) {
            return EMPTY;
          }
          // Determine element name:
          int elementStart = container.getStartOffset()
              + phpScriptRegion.getStart() + tRegion.getStart();
          TextSequence statement = PHPTextSequenceUtilities
              .getStatement(elementStart + tRegion.getLength(),
                  sRegion, true);
          if (statement == null) {
            return EMPTY;
          }
          int endPosition = PHPTextSequenceUtilities
              .readBackwardSpaces(statement, statement.length());
          int startPosition = PHPTextSequenceUtilities
              .readIdentifierStartIndex(phpVersion, statement,
                  endPosition, true);
          String elementName = statement.subSequence(startPosition,
              endPosition).toString();

          // Determine previous word:
          int prevWordEnd = PHPTextSequenceUtilities
              .readBackwardSpaces(statement, startPosition);
          int prevWordStart = PHPTextSequenceUtilities
              .readIdentifierStartIndex(phpVersion, statement,
                  prevWordEnd, false);
          String prevWord = statement.subSequence(prevWordStart,
              prevWordEnd).toString();

          // Determine next word:
          ITextRegion nextRegion = tRegion;
          do {
            nextRegion = phpScriptRegion.getPhpToken(nextRegion
                .getEnd());
            if (!PHPPartitionTypes.isPHPCommentState(nextRegion
                .getType())
                && nextRegion.getType() != PHPRegionTypes.WHITESPACE) {
              break;
            }
          } while (nextRegion.getEnd() < phpScriptRegion.getLength());

          String nextWord = sDoc.get(
              container.getStartOffset()
                  + phpScriptRegion.getStart()
                  + nextRegion.getStart(),
              nextRegion.getTextLength());

          if (elementName.length() > 0) {
            IType containerType = PHPModelUtils.getCurrentType(
                sourceModule, offset);
            if (containerType == null) {
              containerType = PHPModelUtils.getCurrentNamespace(
                  sourceModule, offset);
            }

            // If we are in function declaration:
            if (FUNCTION.equalsIgnoreCase(prevWord)) { //$NON-NLS-1$
              if (containerType != null) {
                return PHPModelUtils.getTypeMethod(
                    containerType, elementName, true);
              }
              return getFunction(sourceModule, elementName);
            }

            // If we are in class declaration:
            if (CLASS.equalsIgnoreCase(prevWord)
                || INTERFACE.equalsIgnoreCase(prevWord)) { //$NON-NLS-1$ //$NON-NLS-2$
              if (containerType != null) {
                if (containerType.getElementName()
                    .equalsIgnoreCase(elementName)) {
                  containerType = PHPModelUtils
                      .getCurrentNamespace(sourceModule,
                          offset);
                }
                if (containerType != null) {
                  return PHPModelUtils.getTypeType(
                      containerType, elementName, true);
                }
              }
              return getClass(sourceModule, elementName);
            }

            // Class instantiation:
            if (NEW.equalsIgnoreCase(prevWord)) { //$NON-NLS-1$
              return getConstructorsIfAny(extractClasses(PHPModelUtils
                  .getTypes(elementName, sourceModule,
                      offset, cache, null)));
            }

            // Handle extends and implements:
            // Check that the statement suites the condition. If
            // class or interface keywords don't appear in the
            // beginning of the statement or they are alone there.
            boolean isClassDeclaration = false;
            if (statement.length() > 6
                && (CLASS.equals(statement.subSequence(0, 5)
                    .toString())
                    && (isClassDeclaration = true) || statement
                    .length() > 10
                    && INTERFACE.equals(statement
                        .subSequence(0, 9).toString()))) { //$NON-NLS-1$ //$NON-NLS-2$

              IModelElement[] generalizationTypes = getGeneralizationTypes(
                  sourceModule, isClassDeclaration, prevWord,
                  elementName, offset);
              if (generalizationTypes != null) {
                return generalizationTypes;
              }

              // Multiple extensions and implementations:
              int listStartPosition = PHPTextSequenceUtilities
                  .readIdentifierListStartIndex(statement,
                      endPosition);

              // Determine pre-list word:
              int preListWordEnd = PHPTextSequenceUtilities
                  .readBackwardSpaces(statement,
                      listStartPosition);
              int preListWordStart = PHPTextSequenceUtilities
                  .readIdentifierStartIndex(statement,
                      preListWordEnd, false);
              String preListWord = statement.subSequence(
                  preListWordStart, preListWordEnd)
                  .toString();

              generalizationTypes = getGeneralizationTypes(
                  sourceModule, isClassDeclaration,
                  preListWord, elementName, offset);
              if (generalizationTypes != null) {
                return generalizationTypes;
              }
            }

            // Previous trigger:
            String trigger = null;
            if (startPosition > 2) {
              trigger = statement.subSequence(startPosition - 2,
                  startPosition).toString();
            }

            // If this is variable:
            if (elementName.charAt(0) == '$'
                && !PAAMAYIM_NEKUDOTAIM.equals(trigger)) { //$NON-NLS-1$
              // Don't show escaped variables within PHP string:
              if (PHPPartitionTypes.isPHPQuotesState(tRegion
                  .getType())) {
                try {
                  char charBefore = sDoc.get(
                      elementStart - 2, 1).charAt(0);
                  if (charBefore == '\\') {
                    return EMPTY;
                  }
                } catch (BadLocationException e) {
                  PHPCorePlugin.log(e);
                }
              }

              // If we are in var definition:
              if (containerType != null) {
                if (VAR.equalsIgnoreCase(prevWord)
                    || PRIVATE.equalsIgnoreCase(prevWord)
                    || STATIC.equalsIgnoreCase(prevWord)
                    || PUBLIC.equalsIgnoreCase(prevWord)
                    || PROTECTED.equalsIgnoreCase(prevWord)) {
                  return PHPModelUtils.getTypeField(
                      containerType, elementName, true);
                }
                if (THIS.equalsIgnoreCase(elementName)) { //$NON-NLS-1$
                  return new IModelElement[] { containerType };
                }
              }

              return getGlobalOrMethodFields(sourceModule,
                  offset, elementName);
            }

            // If we are at class constant definition:
            if (containerType != null) {
              if (CONST.equalsIgnoreCase(prevWord)) { //$NON-NLS-1$
                return PHPModelUtils.getTypeField(
                    containerType, elementName, true);
              }
            }

            // We are at class trigger:
            if (PAAMAYIM_NEKUDOTAIM.equals(nextWord)) { //$NON-NLS-1$
              return PHPModelUtils.getTypes(elementName,
                  sourceModule, offset, cache, null);
            }
            if (NS_SEPARATOR.equals(nextWord)) { //$NON-NLS-1$
              IDLTKSearchScope scope = SearchEngine
                  .createSearchScope(sourceModule
                      .getScriptProject());
              return PhpModelAccess.getDefault().findTypes(null,
                  elementName, MatchRule.EXACT,
                  Modifiers.AccNameSpace, 0, scope, null);
            }

            IType[] types = CodeAssistUtils.getTypesFor(
                sourceModule, statement, startPosition, offset);

            // Is it function or method:
            if (OPEN_BRACE.equals(nextWord)
                || PHPPartitionTypes.isPHPDocState(tRegion
                    .getType())) { //$NON-NLS-1$
              if (types != null && types.length > 0) {
                List<IMethod> methods = new LinkedList<IMethod>();
                for (IType t : types) {
                  methods.addAll(Arrays.asList(PHPModelUtils
                      .getTypeHierarchyMethod(t, cache
                          .getSuperTypeHierarchy(t,
                              null), elementName,
                          true, null)));
                }
                return methods.toArray(new IMethod[methods
                    .size()]);
              }
              return PHPModelUtils.getFunctions(elementName,
                  sourceModule, offset, cache, null);
            }
            if ("insteadof".equals(nextWord) //$NON-NLS-1$
                || "as".equals(nextWord)) { //$NON-NLS-1$
              if (types != null && types.length > 0) {
                List<IMethod> methods = new LinkedList<IMethod>();
                for (IType t : types) {
                  methods.addAll(Arrays.asList(PHPModelUtils
                      .getTypeHierarchyMethod(t, cache
                          .getSuperTypeHierarchy(t,
                              null), elementName,
                          true, null)));
                }
                return methods.toArray(new IMethod[methods
                    .size()]);
              }
            }
            if (types != null && types.length > 0) {
              // Check whether this is a class constant:
              if (startPosition > 0) {
                if (PAAMAYIM_NEKUDOTAIM.equals(trigger)
                    && elementName.charAt(0) != '$') { //$NON-NLS-1$
                  List<IModelElement> fields = new LinkedList<IModelElement>();
                  for (IType t : types) {
                    IField[] typeFields = PHPModelUtils
                        .getTypeField(t, elementName,
                            true);
                    for (IField currentField : typeFields) {
                      fields.add(currentField);
                    }
                  }
                  return fields
                      .toArray(new IModelElement[fields
                          .size()]);
                }
              }

              // What can it be? Only class variables:
              // Set<IModelElement> fields = new
              // TreeSet<IModelElement>(
              // new SourceFieldComparator());
              final List<IField> fields = new ArrayList<IField>();
              for (IType t : types) {
                fields.addAll(Arrays
                    .asList(getTypeHierarchyField(t,
                        cache.getSuperTypeHierarchy(t,
                            null), elementName,
                        true, null)));
              }
              return fields.toArray(new IModelElement[fields
                  .size()]);
            }

            // This can be only global constant, if we've reached
            // here:
            IField[] fields = PHPModelUtils.getFields(elementName,
                sourceModule, offset, cache, null);
            if (fields != null && fields.length > 0) {
              return fields;
            }

            // Return class if nothing else found.
            return PHPModelUtils.getTypes(elementName,
                sourceModule, offset, cache, null);
          }
        }
      }
    } catch (Exception e) {
      PHPCorePlugin.log(e);
    }
    return EMPTY;
  }

  public static IField[] getTypeHierarchyField(IType type,
      ITypeHierarchy hierarchy, String prefix, boolean exactName,
      IProgressMonitor monitor) throws CoreException {
    if (prefix == null) {
      throw new NullPointerException();
    }
    final List<IField> fields = new ArrayList<IField>();
    fields.addAll(Arrays.asList(PHPModelUtils.getTypeField(type, prefix,
        exactName)));
    if (type.getSuperClasses() != null && type.getSuperClasses().length > 0) {
      Set<IModelElement> fieldSet = new TreeSet<IModelElement>(
          new SourceFieldComparator());
      fieldSet.addAll(Arrays.asList(PHPModelUtils
          .getSuperTypeHierarchyField(type, hierarchy, prefix,
              exactName, monitor)));
      IField[] temp = fieldSet.toArray(new IField[fieldSet.size()]);
      for (IField field : temp) {
        fields.add(field);
      }
    }
    return fields.toArray(new IField[fields.size()]);
  }

  private static IModelElement[] getGeneralizationTypes(
      ISourceModule sourceModule, boolean isClassDeclaration,
      String generalization, String elementName, int offset)
      throws ModelException {
    if (EXTENDS.equalsIgnoreCase(generalization)) {
      if (isClassDeclaration) {
        return extractClasses(PHPModelUtils.getTypes(elementName,
            sourceModule, offset, null, null));
      }
      return extractInterfaces(PHPModelUtils.getTypes(elementName,
          sourceModule, offset, null, null));
    }
    if (IMPLEMENTS.equalsIgnoreCase(generalization)) { //$NON-NLS-1$ //$NON-NLS-2$
      return extractInterfaces(PHPModelUtils.getTypes(elementName,
          sourceModule, offset, null, null));
    }
    return null;
  }

  private static IModelElement[] getFunction(ISourceModule sourceModule,
      String elementName) throws ModelException {
    IMethod[] methods = ((AbstractSourceModule) sourceModule).getMethods();
    for (IMethod method : methods) {
      if (method.getElementName().equalsIgnoreCase(elementName)) {
        return new IModelElement[] { method };
      }
    }
    return EMPTY;
  }

  private static IModelElement[] getClass(ISourceModule sourceModule,
      String elementName) throws ModelException {
    IType[] types = sourceModule.getTypes();
    for (IType type : types) {
      if (type.getElementName().equalsIgnoreCase(elementName)) {
        return new IModelElement[] { type };
      }
    }
    return EMPTY;
  }

  private static IType[] extractInterfaces(IType[] types) {
    List<IType> result = new ArrayList<IType>(types.length);
    for (IType type : types) {
      try {
        if (PHPFlags.isInterface(type.getFlags())) {
          result.add(type);
        }
      } catch (ModelException e) {
        PHPCorePlugin.log(e);
      }
    }
    return (IType[]) result.toArray(new IType[result.size()]);
  }

  private static IType[] extractClasses(IType[] types) {
    List<IType> result = new ArrayList<IType>(types.length);
    for (IType type : types) {
      try {
        if (PHPFlags.isClass(type.getFlags())) {
          result.add(type);
        }
      } catch (ModelException e) {
        PHPCorePlugin.log(e);
      }
    }
    return (IType[]) result.toArray(new IType[result.size()]);
  }

  private static IModelElement[] getConstructorsIfAny(IType[] types)
      throws ModelException {
    List<IModelElement> result = new LinkedList<IModelElement>();
    for (IType type : types) {
      boolean hasConstructor = false;
      for (IMethod method : type.getMethods()) {
        if (method.isConstructor()) {
          result.add(method);
          hasConstructor = true;
          break;
        }
      }
      if (!hasConstructor) {
        result.add(type);
      }
    }
    return (IModelElement[]) result
        .toArray(new IModelElement[result.size()]);
  }

  /**
   * Return workspace or method fields depending on current position: whether
   * we are inside method or in global scope.
   *
   * @param sourceModule
   * @param offset
   * @param prefix
   * @param mask
   * @return
   * @throws ModelException
   */
  private static IModelElement[] getGlobalOrMethodFields(
      ISourceModule sourceModule, int offset, String prefix)
      throws ModelException {
    try {
      IModelElement enclosingElement = sourceModule.getElementAt(offset);
      if (enclosingElement instanceof IField) {
        enclosingElement = enclosingElement.getParent();
      }
      if (enclosingElement instanceof IMethod) {
        IMethod method = (IMethod) enclosingElement;
        return PHPModelUtils
            .getMethodFields(method, prefix, true, null);
      }
    } catch (ModelException e) {
      PHPCorePlugin.log(e);
    }
    return PHPModelUtils
        .getFields(prefix, sourceModule, offset, null, null);
  }

  private static class SourceFieldComparator implements
      Comparator<IModelElement> {
    public int compare(IModelElement o1, IModelElement o2) {
      try {
        SourceRefElement e1 = (SourceRefElement) o1;
        SourceRefElement e2 = (SourceRefElement) o2;
        IType type1 = (IType) e1.getAncestor(IModelElement.TYPE);
        IType type2 = (IType) e2.getAncestor(IModelElement.TYPE);
        if (type1 != null && type2 != null && type1 != type2) {
          return -1;
        }
        if (e1.getSourceModule() == e2.getSourceModule()) {
          ISourceRange r1 = e1.getSourceRange();
          ISourceRange r2 = e2.getSourceRange();
          return (int) Math.signum(r1.getOffset() - r2.getOffset());
        } else {
          return -1;
        }
      } catch (ModelException e) {
        PHPCorePlugin.log(e);
      }
      return 0;
    }
  }
}
TOP

Related Classes of org.eclipse.php.internal.core.codeassist.PHPSelectionEngine$SourceFieldComparator

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.