Package com.puppetlabs.geppetto.pp.dsl.linking

Source Code of com.puppetlabs.geppetto.pp.dsl.linking.PPFinder$SearchResult

/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* 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:
*   Puppet Labs
*/
package com.puppetlabs.geppetto.pp.dsl.linking;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.puppetlabs.geppetto.common.tracer.ITracer;
import com.puppetlabs.geppetto.pp.AppendExpression;
import com.puppetlabs.geppetto.pp.AssignmentExpression;
import com.puppetlabs.geppetto.pp.BinaryExpression;
import com.puppetlabs.geppetto.pp.Definition;
import com.puppetlabs.geppetto.pp.DefinitionArgument;
import com.puppetlabs.geppetto.pp.Expression;
import com.puppetlabs.geppetto.pp.HostClassDefinition;
import com.puppetlabs.geppetto.pp.Lambda;
import com.puppetlabs.geppetto.pp.NodeDefinition;
import com.puppetlabs.geppetto.pp.PPPackage;
import com.puppetlabs.geppetto.pp.ResourceBody;
import com.puppetlabs.geppetto.pp.VariableExpression;
import com.puppetlabs.geppetto.pp.dsl.PPDSLConstants;
import com.puppetlabs.geppetto.pp.dsl.adapters.PPImportedNamesAdapter;
import com.puppetlabs.geppetto.pp.dsl.linking.NameInScopeFilter.Match;
import com.puppetlabs.geppetto.pp.dsl.linking.NameInScopeFilter.SearchStrategy;
import com.puppetlabs.geppetto.pp.dsl.linking.PPSearchPath.ISearchPathProvider;
import com.puppetlabs.geppetto.pp.pptp.PPTPPackage;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.IContainer;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;

import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.inject.Inject;
import com.google.inject.name.Named;

/**
* Utility class for finding references.
*
*/
public class PPFinder {
  public static class SearchResult {
    private List<IEObjectDescription> adjusted;

    private List<IEObjectDescription> raw;

    private SearchResult() {
      this.adjusted = Collections.emptyList();
      this.raw = this.adjusted;
    }

    private SearchResult(List<IEObjectDescription> rawAndAdjusted) {
      this.adjusted = this.raw = rawAndAdjusted;
    }

    private SearchResult(List<IEObjectDescription> pathAdjusted, List<IEObjectDescription> raw) {
      this.adjusted = pathAdjusted;
      this.raw = raw;
    }

    private SearchResult addAll(SearchResult other) {
      this.adjusted.addAll(other.adjusted);
      this.raw.addAll(other.raw);
      return this;
    }

    public List<IEObjectDescription> getAdjusted() {
      return adjusted;
    }

    public List<IEObjectDescription> getRaw() {
      return raw;
    }
  }

  private final static EClass[] CLASSES_FOR_VARIABLES = { //
  PPPackage.Literals.DEFINITION_ARGUMENT, //
      PPTPPackage.Literals.TP_VARIABLE, //
      // PPTPPackage.Literals.TYPE_ARGUMENT, //
      PPPackage.Literals.VARIABLE_EXPRESSION };

  private final static EClass[] DEF_AND_TYPE_ARGUMENTS = {
      PPPackage.Literals.DEFINITION_ARGUMENT, PPTPPackage.Literals.TYPE_ARGUMENT };

  // Note that order is important
  private final static EClass[] DEF_AND_TYPE = { PPTPPackage.Literals.TYPE, PPPackage.Literals.DEFINITION };

  private static final EClass[] FUNC = { PPTPPackage.Literals.FUNCTION };

  private final static EClass[] CLASS_AND_TYPE = { PPPackage.Literals.HOST_CLASS_DEFINITION,
  // PPTPPackage.Literals.TYPE
  };

  private Resource resource;

  @Inject
  private ISearchPathProvider searchPathProvider;

  /**
   * Access to container manager for PP language
   */
  @Inject
  private IContainer.Manager manager;

  private PPSearchPath searchPath;

  private Multimap<String, IEObjectDescription> exportedPerLastSegment;

  /**
   * Access to the 'pp' services (container management and more).
   */
  @Inject
  private IResourceServiceProvider resourceServiceProvider;

  /**
   * Access to naming of model elements.
   */
  @Inject
  private IQualifiedNameProvider fqnProvider;

  /**
   * PP FQN to/from Xtext QualifiedName converter.
   */
  @Inject
  private IQualifiedNameConverter converter;

  /**
   * Access to the global index maintained by Xtext, is made via a special (non guice) provider
   * that is aware of the context (builder, dirty editors, etc.). It is used to obtain the
   * index for a particular resource. This special provider is obtained here.
   */
  @Inject
  private org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider indexProvider;

  /**
   * Access to runtime configurable debug trace.
   */
  @Inject
  @Named(PPDSLConstants.PP_DEBUG_LINKER)
  private ITracer tracer;

  private Map<String, IEObjectDescription> metaCache;

  private Map<String, IEObjectDescription> metaVarCache;

  private List<IEObjectDescription> exportedPatternVariables;

  private void buildExportedObjectsIndex(IResourceDescription descr, IResourceDescriptions descriptionIndex) {
    // The current (possibly dirty) exported resources
    IResourceDescription dirty = resourceServiceProvider.getResourceDescriptionManager().getResourceDescription(
      resource);
    String pathToCurrent = resource.getURI().path();

    Multimap<String, IEObjectDescription> map = ArrayListMultimap.create();
    List<IEObjectDescription> patternedVariables = Lists.newArrayList();
    // add all (possibly dirty in global index)
    // check for empty qualified names which may be present in case of syntax errors / while editing etc.
    // empty names are simply skipped (they can not be found anyway).
    //
    for(IEObjectDescription d : dirty.getExportedObjects())
      if(d.getQualifiedName().getSegmentCount() >= 1)
        map.put(d.getQualifiedName().getLastSegment(), d);
    // add all from global index, except those for current resource
    for(IEObjectDescription d : getExportedObjects(descr, descriptionIndex))
      if(!d.getEObjectURI().path().equals(pathToCurrent) && d.getQualifiedName().getSegmentCount() >= 1) {
        // patterned based names are exceptional
        if(d.getUserData(PPDSLConstants.VARIABLE_PATTERN) != null) {
          patternedVariables.add(d);
        }
        else {
          map.put(d.getQualifiedName().getLastSegment(), d);
        }
      }
    exportedPerLastSegment = map;
    exportedPatternVariables = patternedVariables;
  }

  private void cacheMetaParameters(EObject scopeDetermeningObject) {
    metaCache = Maps.newHashMap();
    metaVarCache = Maps.newHashMap();

    Resource scopeDetermeningResource = scopeDetermeningObject.eResource();

    IResourceDescriptions descriptionIndex = indexProvider.getResourceDescriptions(scopeDetermeningResource);
    IResourceDescription descr = descriptionIndex.getResourceDescription(scopeDetermeningResource.getURI());
    if(descr == null)
      return; // give up - some sort of clean build
    EClass wantedType = PPTPPackage.Literals.TYPE_ARGUMENT;
    for(IContainer visibleContainer : manager.getVisibleContainers(descr, descriptionIndex)) {
      for(IEObjectDescription objDesc : visibleContainer.getExportedObjects()) {
        QualifiedName q = objDesc.getQualifiedName();
        if("Type".equals(q.getFirstSegment())) {
          if(wantedType == objDesc.getEClass() || wantedType.isSuperTypeOf(objDesc.getEClass()))
            metaCache.put(q.getLastSegment(), objDesc);
        }
        else if(objDesc.getEClass() == PPTPPackage.Literals.META_VARIABLE) {
          metaVarCache.put(q.getLastSegment(), objDesc);
        }
      }
    }
  }

  public void configure(EObject o) {
    configure(o.eResource());
  }

  public void configure(Resource r) {
    resource = r;
    IResourceDescriptions descriptionIndex = indexProvider.getResourceDescriptions(resource);
    IResourceDescription descr = descriptionIndex.getResourceDescription(resource.getURI());

    // Happens during start/clean in some state
    if(descr == null)
      return;
    manager = resourceServiceProvider.getContainerManager();
    buildExportedObjectsIndex(descr, descriptionIndex);
    searchPath = searchPathProvider.get(r);
  }

  /**
   * Find an attribute being a DefinitionArgument, Property, or Parameter for the given type, or a
   * meta Property or Parameter defined for the type 'Type'.
   *
   * @param scopeDetermeningObject
   * @param fqn
   * @return
   */
  public SearchResult findAttributes(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames) {
    SearchResult result = null;

    // do meta lookup first as this is made fast via a cache and these are used more frequent
    // than other parameters (measured).
    if(metaCache == null)
      cacheMetaParameters(scopeDetermeningObject);
    IEObjectDescription d = metaCache.get(fqn.getLastSegment());
    if(d == null)
      result = findInherited(
        scopeDetermeningObject, fqn, importedNames, Lists.<QualifiedName> newArrayList(), Match.EQUALS,
        DEF_AND_TYPE_ARGUMENTS);
    else
      result = new SearchResult(Lists.newArrayList(d));
    return result;
  }

  /**
   * @param resourceBody
   * @param fqn
   * @return
   */
  public SearchResult findAttributesWithPrefix(ResourceBody resourceBody, QualifiedName fqn) {
    // Must be configured for the resource containing resourceBody
    List<IEObjectDescription> result = Lists.newArrayList();

    // do meta lookup first as this is made fast via a cache and these are used more frequent
    // than other parameters (measured).
    // TODO: VERIFY that empty last segment matches ok
    // TODO: Make sure that length of match is same number of segments
    if(metaCache == null)
      cacheMetaParameters(resourceBody);
    String fqnLast = fqn.getLastSegment();
    for(String name : metaCache.keySet())
      if(name.startsWith(fqnLast))
        result.add(metaCache.get(name));

    result.addAll(findInherited(
      resourceBody, fqn, null, Lists.<QualifiedName> newArrayList(), Match.STARTS_WITH, DEF_AND_TYPE_ARGUMENTS).getAdjusted());
    return new SearchResult(result);

  }

  public SearchResult findDefinitions(EObject scopeDetermeningResource, PPImportedNamesAdapter importedNames) {
    // make all segments initial char lower case (if references is to the type itself - eg. 'File' instead of
    // 'file', or 'Aa::Bb' instead of 'aa::bb'
    QualifiedName fqn2 = QualifiedName.EMPTY;

    // TODO: Note that order is important, TYPE has higher precedence and should be used for linking
    // This used to work when list was iterated per type, not it is iterated once with type check
    // first - thus if a definition is found before a type, it is earlier in the list.
    return findExternal(scopeDetermeningResource, fqn2, importedNames, Match.STARTS_WITH, DEF_AND_TYPE);
  }

  public SearchResult findDefinitions(EObject scopeDetermeningResource, String name,
      PPImportedNamesAdapter importedNames) {
    if(name == null)
      throw new IllegalArgumentException("name is null");
    QualifiedName fqn = converter.toQualifiedName(name);
    // make all segments initial char lower case (if references is to the type itself - eg. 'File' instead of
    // 'file', or 'Aa::Bb' instead of 'aa::bb'
    QualifiedName fqn2 = QualifiedName.EMPTY;
    for(int i = 0; i < fqn.getSegmentCount(); i++)
      fqn2 = fqn2.append(toInitialLowerCase(fqn.getSegment(i)));
    // fqn2 = fqn.skipLast(1).append(toInitialLowerCase(fqn.getLastSegment()));

    // TODO: Note that order is important, TYPE has higher precedence and should be used for linking
    // This used to work when list was iterated per type, not it is iterated once with type check
    // first - thus if a definition is found before a type, it is earlier in the list.
    return findExternal(scopeDetermeningResource, fqn2, importedNames, Match.EQUALS, DEF_AND_TYPE);
  }

  private SearchResult findExternal(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames, SearchStrategy matchingStrategy, EClass... eClasses) {
    if(scopeDetermeningObject == null)
      throw new IllegalArgumentException("scope determening object is null");
    if(fqn == null)
      throw new IllegalArgumentException("name is null");
    if(eClasses == null || eClasses.length < 1)
      throw new IllegalArgumentException("eClass is null or empty");

    // if(fqn.getSegmentCount() == 1 && "".equals(fqn.getSegment(0)))
    // throw new IllegalArgumentException("FQN has one empty segment");

    List<IEObjectDescription> targets = Lists.newArrayList();
    Resource scopeDetermeningResource = scopeDetermeningObject.eResource();

    // Not meaningful to continue if the name is empty (looking up nothing)
    if(fqn.getSegmentCount() == 0)
      return new SearchResult(targets, targets);

    // Not meaningful to record the fact that an Absolute reference was used as nothing
    // is named with an absolute FQN (i.e. it is only used to do lookup).
    final boolean absoluteFQN = fqn.getSegmentCount() > 0 && "".equals(fqn.getSegment(0));
    if(importedNames != null)
      importedNames.add(absoluteFQN
          ? fqn.skipFirst(1)
          : fqn);

    if(scopeDetermeningResource != resource) {
      // This is a lookup in the perspective of some other resource
      // GIVE UP (the system is cleaning / is in bad state).
      if(resource == null || scopeDetermeningResource == null)
        return new SearchResult(targets, targets);

      IResourceDescriptions descriptionIndex = indexProvider.getResourceDescriptions(scopeDetermeningResource);
      IResourceDescription descr = descriptionIndex.getResourceDescription(scopeDetermeningResource.getURI());

      // GIVE UP (the system is performing a build clean).
      if(descr == null)
        return new SearchResult(targets, targets);
      QualifiedName nameOfScope = getNameOfScope(scopeDetermeningObject);

      // for(IContainer visibleContainer : manager.getVisibleContainers(descr, descriptionIndex)) {
      // for(EClass aClass : eClasses)
      for(IEObjectDescription objDesc : new NameInScopeFilter(matchingStrategy, getExportedObjects(
        descr, descriptionIndex),
      // visibleContainer.getExportedObjects(),
      fqn, nameOfScope, eClasses))
        targets.add(objDesc);
    }
    else {
      // This is lookup from the main resource perspective
      QualifiedName nameOfScope = getNameOfScope(scopeDetermeningObject);
      for(IEObjectDescription objDesc : new NameInScopeFilter(matchingStrategy, //
        matchingStrategy.matchStartsWith()
            ? exportedPerLastSegment.values()
            : exportedPerLastSegment.get(fqn.getLastSegment()), //
        fqn, nameOfScope, eClasses))
        targets.add(objDesc);

      if(targets.size() == 0) {
        // check the pattern variables
        for(IEObjectDescription objDesc : exportedPatternVariables) {
          String n = fqn.getLastSegment();
          String on = objDesc.getName().getLastSegment();
          if(n.startsWith(on) &&
              Pattern.matches(
                objDesc.getUserData(PPDSLConstants.VARIABLE_PATTERN), n.substring(on.length())))
            targets.add(objDesc);

        }
      }
    }
    if(tracer.isTracing()) {
      for(IEObjectDescription d : targets)
        tracer.trace("    : ", converter.toString(d.getName()), " in: ", d.getEObjectURI().path());
    }
    return new SearchResult(searchPathAdjusted(targets), targets);
  }

  public SearchResult findFunction(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames) {
    return findExternal(scopeDetermeningObject, fqn, importedNames, Match.EQUALS, FUNC);
  }

  public SearchResult findFunction(EObject scopeDetermeningObject, String name, PPImportedNamesAdapter importedNames) {
    return findFunction(scopeDetermeningObject, converter.toQualifiedName(name), importedNames);
  }

  public SearchResult findHostClasses(EObject scopeDetermeningResource, String name,
      PPImportedNamesAdapter importedNames) {
    if(name == null)
      throw new IllegalArgumentException("name is null");
    QualifiedName fqn = converter.toQualifiedName(name);
    // make last segments initial char lower case (for references to the type itself - eg. 'File' instead of
    // 'file'.
    if(fqn.getSegmentCount() == 0)
      return new SearchResult(); // can happen while editing
    fqn = fqn.skipLast(1).append(toInitialLowerCase(fqn.getLastSegment()));
    return findExternal(scopeDetermeningResource, fqn, importedNames, Match.EQUALS, CLASS_AND_TYPE);
  }

  private SearchResult findInherited(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames, List<QualifiedName> stack, SearchStrategy matchingStrategy,
      EClass[] classes) {
    // Protect against circular inheritance
    QualifiedName containerName = fqn.skipLast(1);
    if(stack.contains(containerName))
      return new SearchResult();
    stack.add(containerName);

    // find using the given name
    SearchResult searchResult = findExternal(scopeDetermeningObject, fqn, importedNames, matchingStrategy, classes);
    final List<IEObjectDescription> result = searchResult.getAdjusted();
    // Collect raw results to enable better error reporting on path errors
    List<IEObjectDescription> rawResult = Lists.newArrayList();
    rawResult.addAll(searchResult.getRaw());

    // Search up the inheritance chain if no match (on exact match), or if a prefix search
    if(result.isEmpty() || !matchingStrategy.isExists()) {
      // find the parent type
      if(containerName.getSegmentCount() > 0) {
        // there was a parent
        List<IEObjectDescription> parentResult = findExternal(
          scopeDetermeningObject, containerName, importedNames, Match.EQUALS, DEF_AND_TYPE).getAdjusted();
        if(!parentResult.isEmpty()) {
          IEObjectDescription firstFound = parentResult.get(0);
          String parentName = firstFound.getUserData(PPDSLConstants.PARENT_NAME_DATA);
          if(parentName != null && parentName.length() > 0) {
            // find attributes for parent

            QualifiedName attributeFqn = converter.toQualifiedName(parentName);
            attributeFqn = attributeFqn.append(fqn.getLastSegment());
            SearchResult inheritedSearchResult = findInherited(
              scopeDetermeningObject, attributeFqn, importedNames, stack, matchingStrategy, classes);
            result.addAll(inheritedSearchResult.getAdjusted());
            rawResult.addAll(inheritedSearchResult.getRaw());
          }
        }
      }
    }
    return new SearchResult(result, rawResult);
  }

  public IEObjectDescription findLocalVariableInLambda(Lambda lambda, String name,
      PPImportedNamesAdapter importedNames, SearchStrategy matchingStrategy) {
    TreeIterator<EObject> itor = lambda.eAllContents();
    while(itor.hasNext()) {
      EObject o = itor.next();
      if(o instanceof AssignmentExpression || o instanceof AppendExpression) {
        Expression lhs = ((BinaryExpression) o).getLeftExpr();
        if(lhs instanceof VariableExpression) {
          String vname = ((VariableExpression) lhs).getVarName();
          if(vname.startsWith("$"))
            if(vname.length() > 1)
              vname = name.substring(1);
            else
              vname = "";
          if(name.equals(vname))
            return EObjectDescription.create(name, lhs);
        }
      }
      else if(o instanceof DefinitionArgument) {
        DefinitionArgument arg = (DefinitionArgument) o;
        String argName = arg.getArgName();
        if(argName.startsWith("$") && name.equals(argName.substring(1)) || name.equals(argName))
          return EObjectDescription.create(name, o);
      }
      else if(o instanceof Lambda) {
        itor.prune(); // do not visit nested Lambdas
      }
    }
    return null;
  }

  public IEObjectDescription findLocalVariables(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames, SearchStrategy matchingStrategy) {
    if(fqn.getSegmentCount() != 1)
      return null;
    String name = fqn.getFirstSegment();
    if(name == null || name.length() < 1)
      return null;
    // find enclosing lambda
    for(EObject container = scopeDetermeningObject; container != null; container = container.eContainer()) {
      if(container instanceof Lambda) {
        IEObjectDescription desc = findLocalVariableInLambda(
          (Lambda) container, name, importedNames, matchingStrategy);
        if(desc != null)
          return desc;
      }
      if(container instanceof HostClassDefinition)
        break;
      if(container instanceof Definition)
        break;
      if(container instanceof NodeDefinition)
        break;
    }
    return null;
  }

  /**
   * Finds a parameter or variable with the given name. More than one may be returned if the definition
   * is ambiguous.
   *
   * @param scopeDetermeningResource
   * @param name
   * @param importedNames
   * @return
   */
  public SearchResult findVariable(EObject scopeDetermeningResource, QualifiedName fqn,
      PPImportedNamesAdapter importedNames) {
    if(fqn == null)
      throw new IllegalArgumentException("fqn is null");
    return findVariables(scopeDetermeningResource, fqn, importedNames, Match.NO_OUTER);
  }

  /**
   * Finds a parameter or variable with the given name. More than one may be returned if the definition
   * is ambiguous.
   *
   * @param scopeDetermeningResource
   * @param name
   * @param importedNames
   * @return
   */
  public SearchResult findVariable(EObject scopeDetermeningResource, String name, PPImportedNamesAdapter importedNames) {
    if(name == null)
      throw new IllegalArgumentException("name is null");
    QualifiedName fqn = converter.toQualifiedName(name);
    return findVariables(scopeDetermeningResource, fqn, importedNames, Match.NO_OUTER_EXISTS);
  }

  /**
   * Finds all matching variables in current and inherited scopes.
   *
   * @param scopeDetermeningResource
   * @param name
   * @param importedNames
   * @return
   */
  public SearchResult findVariables(EObject scopeDetermeningResource, QualifiedName fqn,
      PPImportedNamesAdapter importedNames) {
    if(fqn == null)
      throw new IllegalArgumentException("name is null");
    return findVariables(scopeDetermeningResource, fqn, importedNames, Match.NO_OUTER);
  }

  /**
   * Fins parameters and/or variables with matching name using the given matching/search strategy.
   *
   * @param scopeDetermeningObject
   * @param fqn
   * @param importedNames
   * @param matchingStrategy
   * @return
   */
  public SearchResult findVariables(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames, SearchStrategy matchingStrategy) {
    if(metaCache == null)
      cacheMetaParameters(scopeDetermeningObject);

    final boolean singleSegment = fqn.getSegmentCount() == 1;

    // If variable is a meta var, it is always found
    if(singleSegment) {
      IEObjectDescription metaVar = metaVarCache.get(fqn.getLastSegment());
      if(metaVar != null)
        return new SearchResult(Lists.newArrayList(metaVar), Lists.newArrayList(metaVar)); // what a waste...

      // if inside a define, all meta parameters are available
      if(isContainedInDefinition(scopeDetermeningObject)) {
        IEObjectDescription metaParam = metaCache.get(fqn.getLastSegment());
        if(metaParam != null)
          return new SearchResult(Lists.newArrayList(metaParam), Lists.newArrayList(metaParam)); // what a waste...
      }
      IEObjectDescription desc = findLocalVariables(scopeDetermeningObject, fqn, importedNames, matchingStrategy);
      if(desc != null)
        return new SearchResult(Lists.newArrayList(desc));

    }
    SearchResult result = findExternal(
      scopeDetermeningObject, fqn, importedNames, matchingStrategy, CLASSES_FOR_VARIABLES);
    if(result.getAdjusted().size() > 0 && matchingStrategy.isExists())
      return result;
    QualifiedName scopeName = getNameOfScope(scopeDetermeningObject);
    if(!scopeName.isEmpty()) {
      fqn = scopeName.append(fqn);
      return result.addAll(findInherited(
        scopeDetermeningObject, fqn, importedNames, Lists.<QualifiedName> newArrayList(), matchingStrategy,
        CLASSES_FOR_VARIABLES));
    }
    return result;
  }

  /**
   * Finds all matching variables in current and inherited scopes.
   *
   * @param scopeDetermeningResource
   * @param name
   * @param importedNames
   * @return
   */
  public SearchResult findVariables(EObject scopeDetermeningResource, String name,
      PPImportedNamesAdapter importedNames) {
    if(name == null)
      throw new IllegalArgumentException("name is null");
    QualifiedName fqn = converter.toQualifiedName(name);
    return findVariables(scopeDetermeningResource, fqn, importedNames, Match.NO_OUTER);
  }

  public SearchResult findVariablesPrefixed(EObject scopeDetermeningObject, QualifiedName fqn,
      PPImportedNamesAdapter importedNames) {
    return findVariables(scopeDetermeningObject, fqn, importedNames, Match.NO_OUTER_STARTS_WITH);
  }

  /**
   * Produces an unmodifiable list of everything visible to the resource.
   *
   * @return
   */
  public Collection<IEObjectDescription> getExportedDescriptions() {
    return Collections.unmodifiableCollection(exportedPerLastSegment.values());
  }

  /**
   * Produces iterable over all visible exports from all visible containers.
   *
   * @param descr
   * @param descriptionIndex
   * @return
   */
  private Iterable<IEObjectDescription> getExportedObjects(IResourceDescription descr,
      IResourceDescriptions descriptionIndex) {
    return Iterables.concat(Iterables.transform(
      manager.getVisibleContainers(descr, descriptionIndex),
      new Function<IContainer, Iterable<IEObjectDescription>>() {

        @Override
        public Iterable<IEObjectDescription> apply(IContainer from) {
          return from.getExportedObjects();
        }

      }));
  }

  public Collection<IEObjectDescription> getExportedPatternVariableDescriptions() {
    return Collections.unmodifiableCollection(exportedPatternVariables);
  }

  /**
   * Produces an unmodifiable Multimap mapping from last segment to list of visible exports
   * ending with that value.
   *
   * @return
   */
  public Multimap<String, IEObjectDescription> getExportedPerLastSegement() {
    return Multimaps.unmodifiableMultimap(exportedPerLastSegment);
  }

  /**
   * Produces the name of the scope where the given object 'o' is contained.
   *
   * @param o
   * @return
   */
  public QualifiedName getNameOfScope(EObject o) {
    QualifiedName result = null;
    for(; o != null; o = o.eContainer()) {
      result = fqnProvider.getFullyQualifiedName(o);
      if(result != null)
        return result;
    }
    return QualifiedName.EMPTY;
  }

  private boolean isContainedInDefinition(EObject scoped) {
    for(EObject o = scoped; o != null; o = o.eContainer())
      if(o.eClass() == PPPackage.Literals.DEFINITION)
        return true;
    return false;
  }

  /**
   * Adjusts the list of found targets in accordance with the search path for the resource being
   * linked. This potentially resolves ambiguities (if found result is further away on the path).
   * May return more than one result, if more than one resolution exist with the same path index.
   *
   * @param targets
   * @return list of descriptions with lowest index.
   */
  private List<IEObjectDescription> searchPathAdjusted(List<IEObjectDescription> targets) {
    int minIdx = Integer.MAX_VALUE;
    List<IEObjectDescription> result = Lists.newArrayList();
    for(IEObjectDescription d : targets) {
      int idx = searchPath.searchIndexOf(d);
      if(idx < 0)
        continue; // not found, skip
      if(idx < minIdx) {
        minIdx = idx;
        result.clear(); // forget worse result
      }
      // only remember if equal to best found so far
      if(idx <= minIdx)
        result.add(d);
    }
    return result;
  }

  private String toInitialLowerCase(String s) {
    if(s.length() < 1 || Character.isLowerCase(s.charAt(0)))
      return s;
    StringBuilder buf = new StringBuilder(s);
    buf.setCharAt(0, Character.toLowerCase(buf.charAt(0)));
    return buf.toString();
  }
}
TOP

Related Classes of com.puppetlabs.geppetto.pp.dsl.linking.PPFinder$SearchResult

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.