blob: 8473c3d5608c94260058183db1e6853e5d0536be [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.flex.compiler.internal.legacy;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.flex.compiler.common.ASModifier;
import org.apache.flex.compiler.common.ModifiersSet;
import org.apache.flex.compiler.common.RecursionGuard;
import org.apache.flex.compiler.constants.IASLanguageConstants;
import org.apache.flex.compiler.constants.IMetaAttributeConstants;
import org.apache.flex.compiler.constants.INamespaceConstants;
import org.apache.flex.compiler.definitions.IAccessorDefinition;
import org.apache.flex.compiler.definitions.IClassDefinition;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.IFunctionDefinition;
import org.apache.flex.compiler.definitions.IGetterDefinition;
import org.apache.flex.compiler.definitions.IInterfaceDefinition;
import org.apache.flex.compiler.definitions.IMetadataDefinition;
import org.apache.flex.compiler.definitions.INamespaceDefinition;
import org.apache.flex.compiler.definitions.IPackageDefinition;
import org.apache.flex.compiler.definitions.ISetterDefinition;
import org.apache.flex.compiler.definitions.ITypeDefinition;
import org.apache.flex.compiler.definitions.IVariableDefinition;
import org.apache.flex.compiler.definitions.metadata.IMetaTag;
import org.apache.flex.compiler.definitions.references.INamespaceReference;
import org.apache.flex.compiler.internal.definitions.AppliedVectorDefinition;
import org.apache.flex.compiler.internal.definitions.ClassDefinition;
import org.apache.flex.compiler.internal.definitions.ClassDefinitionBase;
import org.apache.flex.compiler.internal.definitions.InterfaceDefinition;
import org.apache.flex.compiler.internal.definitions.NamespaceDefinition;
import org.apache.flex.compiler.internal.definitions.ScopedDefinitionBase;
import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase;
import org.apache.flex.compiler.internal.scopes.ASFileScope;
import org.apache.flex.compiler.internal.scopes.ASScope;
import org.apache.flex.compiler.internal.scopes.ASScopeBase;
import org.apache.flex.compiler.internal.scopes.MXMLFileScope;
import org.apache.flex.compiler.internal.scopes.ScopeView;
import org.apache.flex.compiler.internal.scopes.TypeScope;
import org.apache.flex.compiler.internal.tree.as.FileNode;
import org.apache.flex.compiler.internal.tree.as.IdentifierNode;
import org.apache.flex.compiler.internal.tree.as.MemberAccessExpressionNode;
import org.apache.flex.compiler.internal.workspaces.Workspace;
import org.apache.flex.compiler.projects.ICompilerProject;
import org.apache.flex.compiler.scopes.IASScope;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IClassNode;
import org.apache.flex.compiler.tree.as.IDefinitionNode;
import org.apache.flex.compiler.tree.as.IExpressionNode;
import org.apache.flex.compiler.units.ICompilationUnit;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
/**
* An {@link ASDefinitionFilter} provides the ability to restrict and search
* specifically for various types of definitions encountered when looking
* through symbol tables kept inside of scopes.
* <p>
* an {@link ASDefinitionFilter} provides the ability to search based on:
* <ul>
* <li>Classification of the {@link IDefinition} through
* {@link ClassificationValue}. For example, we can search for functions but not
* getters</li>
* <li>Scope to search in, such as containing, inherited, etc</li>
* <li>Modifiers on an {@link IDefinition}</li>
* <li>Namespace of an {@link IDefinition}</li>
* </ul>
* </p>
* <p>
* While it is possible to build an {@link ASDefinitionFilter} directly, a
* number of static factory methods exist to build specific filters, such as
* filters that know how to find members of types, or find all classes for
* example. When possible, those filters should be used to guarantee correct
* filtering behavior.
* </p>
*/
public class ASDefinitionFilter
{
/**
* Cache object that preserves a specific filter for use later, removing any
* references to {@link IASNode} contexts. When finished with a query, the
* context should be removed by calling clearContext(()
*/
public static final class CachedDefinitionFilter
{
private ASDefinitionFilter fFilter;
CachedDefinitionFilter(ASDefinitionFilter filter)
{
fFilter = filter;
fFilter.fContext = null;
}
/**
* Returns the underlying {@link ASDefinitionFilter} and sets the
* current context.
*
* @param context the {@link IASNode} representing the current context
* @return the underlying {@link ASDefinitionFilter}
*/
public ASDefinitionFilter getDefinitionFilter(IASNode context)
{
fFilter.fContext = new NodeFilterContext(context);
return fFilter;
}
public ASDefinitionFilter getDefinitionFilter(IDefinition context)
{
fFilter.fContext = new DefinitionFilterContext(context);
return fFilter;
}
/**
* Removes any context associated with this filter
*/
public void clearContext()
{
fFilter.fContext = null;
}
}
/**
* Enum that determines where a search should look
*/
public static enum SearchScopeValue
{
/**
* Search for definitions in this scope
*/
IMMEDIATE_MEMBERS,
/**
* Search this scope plus the base class scope
*/
INHERITED_MEMBERS,
/**
* Search all available scopes
*/
ALL_SCOPES
{
@Override
public boolean searchAllNamespaces()
{
return false;
}
},
/**
* Search scopes up to the parent container
*/
CONTAINING_SCOPES
{
@Override
public boolean searchAllNamespaces()
{
return false;
}
};
public boolean searchAllNamespaces()
{
return true;
}
}
protected SearchScopeValue fSearchScopeRule;
/**
* Classification of definitions that guides an {@link ASDefinitionFilter}
*/
public static enum ClassificationValue
{
FUNCTIONS,
GETTERS,
SETTERS,
ACCESSORS,
VARIABLES,
META_TAG_TYPES,
CLASSES,
INTERFACES,
CLASSES_AND_INTERFACES,
PACKAGES,
CLASSES_INTERFACES_AND_PACKAGES,
CLASSES_INTERFACES_AND_NAMESPACES,
CLASSES_AND_PACKAGES,
INTERFACES_AND_PACKAGES,
OBJECTS, // variables, classes, or packages
ALL, // functions, variables, classes, or packages
ALL_NO_ACCESSORS, // functions, variables, classes, or packages, but no accessor functions
NAMESPACES,
MEMBERS_AND_TYPES,
VARIABLES_AND_FUNCTIONS,
TYPES_FUNCTIONS_AND_VARIABLES,
CLASSES_INTERFACES_AND_FUNCTIONS
}
/**
* The classification that we are searching for
*/
protected ClassificationValue fClassificationRule;
/**
* IFilter for namespaces
*/
public static class AccessValue
{
private static class SpecialAccessValue extends AccessValue
{
public SpecialAccessValue(String name)
{
super((INamespaceDefinition)null);
this.name = name;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
return this == obj;
}
private final String name;
/**
* {@inheritDoc}
* <p>
* For debugging.
*/
@Override
public String toString()
{
return "SpecialAccessValue(" + name + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
public final static AccessValue ALL = new SpecialAccessValue("all"); //$NON-NLS-1$
public final static AccessValue INCLUDED = new SpecialAccessValue("included"); //$NON-NLS-1$
public final static AccessValue INTERNAL = new SpecialAccessValue(INamespaceConstants.internal_);
public final static AccessValue PRIVATE = new SpecialAccessValue(INamespaceConstants.private_);
public final static AccessValue PROTECTED = new SpecialAccessValue(INamespaceConstants.protected_);
public final static AccessValue PUBLIC = new SpecialAccessValue(INamespaceConstants.public_);
//make this the same as public
public final static AccessValue AS3 = new AccessValue(NamespaceDefinition.getAS3NamespaceDefinition());
INamespaceDefinition namespaceDef = null;
public AccessValue(INamespaceDefinition n)
{
this.namespaceDef = n;
}
/**
* Return a namespace for the access value. May be null
*
* @return INamespaceDefinition
*/
public INamespaceDefinition getNamespaceDef()
{
return namespaceDef;
}
/**
* {@inheritDoc}
* <p>
* For debugging.
*/
@Override
public String toString()
{
return "AccessValue(" + (namespaceDef != null ? namespaceDef.toString() : "null") + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode()
{
if (namespaceDef == null)
return super.hashCode();
return namespaceDef.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
if (obj == this)
return true;
if (obj instanceof SpecialAccessValue)
return obj.equals(this);
if (obj instanceof AccessValue)
{
AccessValue that = (AccessValue)obj;
if ((that.namespaceDef == namespaceDef) ||
((namespaceDef != null) && (namespaceDef.equals(that.namespaceDef))))
return true;
}
return false;
}
@Deprecated
public static AccessValue unionAccessRules(AccessValue oldAccessValue, AccessValue newAccessValue)
{
if (!(oldAccessValue instanceof AccessValue.SpecialAccessValue))
return oldAccessValue;
if (!(newAccessValue instanceof AccessValue.SpecialAccessValue))
return newAccessValue;
if ((oldAccessValue == AccessValue.ALL) || (newAccessValue == AccessValue.ALL))
return AccessValue.ALL;
if ((oldAccessValue == AccessValue.INTERNAL) || (newAccessValue == AccessValue.INTERNAL))
return AccessValue.INTERNAL;
if ((oldAccessValue == AccessValue.PRIVATE) || (newAccessValue == AccessValue.PRIVATE))
return AccessValue.PRIVATE;
if ((oldAccessValue == AccessValue.PROTECTED) || (newAccessValue == AccessValue.PROTECTED))
return AccessValue.PROTECTED;
if ((oldAccessValue == AccessValue.PUBLIC) || (newAccessValue == AccessValue.PUBLIC))
return AccessValue.PUBLIC;
return newAccessValue;
}
static public AccessValue createAccessRule(INamespaceDefinition ns)
{
if (ns != null && ns instanceof NamespaceDefinition)
{
if (ns instanceof NamespaceDefinition.IPublicNamespaceDefinition)
return AccessValue.PUBLIC;
else if (ns instanceof NamespaceDefinition.IPrivateNamespaceDefinition)
return AccessValue.PRIVATE;
else if (ns instanceof NamespaceDefinition.IProtectedNamespaceDefinition)
return AccessValue.PROTECTED;
else if (ns instanceof NamespaceDefinition.IInternalNamespaceDefinition)
return AccessValue.INTERNAL;
else if (ns == NamespaceDefinition.getAS3NamespaceDefinition())
return AccessValue.AS3;
AccessValue accessValue = new AccessValue(ns);
return accessValue;
}
return AccessValue.PUBLIC;
}
}
/**
* AccessRule to be used by this filter
*/
protected AccessValue fAccessRule;
/**
* AccessRule to be used by this filter in project-level scope. If not set,
* the normal access value will be used
*/
protected AccessValue fProjectAccessRule;
/**
* Indicate whether or not we require imports (and for which definitions)
*/
public static enum RequireImportsValue
{
YES
{
@Override
public boolean searchAllNamespaces()
{
return false;
}
},
NO,
ONLY_FOR_FUNCTIONS_AND_VARIABLES;
public boolean searchAllNamespaces()
{
return true;
}
}
protected RequireImportsValue fRequireImportsRule;
/**
* Current package name (so that internals from the given package can be
* included) based on the context
*/
private String fPackageName;
/**
* Context (for determining imports)
*/
protected IFilterContext fContext;
/**
* Flag indicating whether or not we should include implicit types (this,
* super, cast functions) and constructors
*/
protected boolean fIncludeImplicitsAndConstructors;
/**
* Modifiers (e.g. static, final, override) that must be either present or
* missing on all filtered definitions
*/
protected HashMap<ASModifier, Boolean> fRequiredAndExcludedModifiers;
/**
* True if we should include excluded items. Off by default
*/
protected Boolean fIncludeExcluded = null;
/**
* Constructor.
*
* @param classification classification (functions, classes, all, etc)
* @param searchScope search scope (all, immediate members, etc)
* @param access access filter (private, public, etc)
* @param context the context node (to be used to determine the current
* imports and package)
*/
@Deprecated
public ASDefinitionFilter(ClassificationValue classification,
SearchScopeValue searchScope,
AccessValue access, IASNode context)
{
this(classification, searchScope, access, new NodeFilterContext(context));
}
/**
* Constructor.
*
* @param classification classification (functions, classes, all, etc)
* @param searchScope search scope (all, immediate members, etc)
* @param access access filter (private, public, etc)
* @param context the context node (to be used to determine the current
* imports and package)
*/
public ASDefinitionFilter(ClassificationValue classification,
SearchScopeValue searchScope,
AccessValue access, IDefinition context)
{
this(classification, searchScope, access, new DefinitionFilterContext(context));
}
/**
* Constructor.
*
* @param classification classification (functions, classes, all, etc)
* @param searchScope search scope (all, immediate members, etc)
* @param access access filter (private, public, etc)
* @param context the context node (to be used to determine the current
* imports and package)
*/
public ASDefinitionFilter(ClassificationValue classification,
SearchScopeValue searchScope,
AccessValue access, IFilterContext context)
{
fClassificationRule = classification;
fSearchScopeRule = searchScope;
fAccessRule = access;
fContext = context;
fRequireImportsRule = RequireImportsValue.YES;
fIncludeImplicitsAndConstructors = true;
}
/**
* Builds a filter that will find the given definition
*
* @param definition the {@link IDefinition} we want to eventually find
*/
public ASDefinitionFilter(IDefinition definition, SearchScopeValue scope)
{
fIncludeImplicitsAndConstructors = true;
fSearchScopeRule = scope;
fContext = new DefinitionFilterContext(definition);
if (definition instanceof IClassDefinition)
fClassificationRule = ClassificationValue.CLASSES;
else if (definition instanceof IInterfaceDefinition)
fClassificationRule = ClassificationValue.INTERFACES;
else if (definition instanceof IPackageDefinition)
fClassificationRule = ClassificationValue.PACKAGES;
else if (definition instanceof ISetterDefinition)
fClassificationRule = ClassificationValue.SETTERS;
else if (definition instanceof IGetterDefinition)
fClassificationRule = ClassificationValue.GETTERS;
else if (definition instanceof IVariableDefinition)
fClassificationRule = ClassificationValue.VARIABLES;
else if (definition instanceof INamespaceDefinition)
fClassificationRule = ClassificationValue.NAMESPACES;
else if (definition instanceof IFunctionDefinition)
fClassificationRule = ClassificationValue.FUNCTIONS;
else if (definition instanceof IMetadataDefinition)
fClassificationRule = ClassificationValue.META_TAG_TYPES;
ICompilerProject compilerProject = findProjectForDefinition(definition);
fAccessRule = AccessValue.createAccessRule(definition.resolveNamespace(compilerProject));
ModifiersSet modifiersSet = definition.getModifiers();
if (modifiersSet != null)
{
ASModifier[] modifiers = modifiersSet.getAllModifiers();
for (int i = 0; i < modifiers.length; i++)
{
setRequiredModifier(modifiers[i]);
}
}
}
/**
* Copy constructor
*
* @param other definition filter to copy
*/
public ASDefinitionFilter(ASDefinitionFilter other)
{
fClassificationRule = other.fClassificationRule;
fSearchScopeRule = other.fSearchScopeRule;
fAccessRule = other.fAccessRule;
fPackageName = other.fPackageName;
fContext = other.fContext;
fRequireImportsRule = other.fRequireImportsRule;
fIncludeImplicitsAndConstructors = true; //TODO [dz] FIX THIS IT SEEMS WRONG
if (other.fRequiredAndExcludedModifiers != null)
{
fRequiredAndExcludedModifiers =
new HashMap<ASModifier, Boolean>(other.fRequiredAndExcludedModifiers);
}
fProjectAccessRule = other.fProjectAccessRule;
fIncludeExcluded = other.fIncludeExcluded;
}
/**
* caches the current {@link ASDefinitionFilter} so that it can be stored
* and used later
*
* @return a {@link CachedDefinitionFilter}
*/
public CachedDefinitionFilter cacheFilter()
{
return new CachedDefinitionFilter(this);
}
/**
* Indicate whether to limit the search to this scope, this scope plus the
* base class scope, or all available scopes
*
* @param searchScopeRule e.g. inherited members, containing scope, etc
*/
public void setSearchScopeRule(SearchScopeValue searchScopeRule)
{
fSearchScopeRule = searchScopeRule;
}
/**
* Indicate which definitions of symbols to search for
*
* @param classification e.g. variables, functions, etc
*/
public void setClassification(ClassificationValue classification)
{
fClassificationRule = classification;
}
/**
* Get which definitions of symbols this filter is searching for
*
* @return e.g. variables, functions, etc
*/
public ClassificationValue getClassification()
{
return fClassificationRule;
}
/**
* Indicate whether to find private members, public members, namespace
* members, etc
*
* @param accessRule e.g. private, public, etc
*/
public void setPrimaryAccessRule(AccessValue accessRule)
{
fAccessRule = accessRule;
}
/**
* * Indicate whether to find private members, public members, namespace
* members, etc in project-level scope
*
* @param accessRule
*/
public void setProjectAccessRule(AccessValue accessRule)
{
fProjectAccessRule = accessRule;
}
public AccessValue getProjectAccessRule()
{
return fProjectAccessRule;
}
/**
* Returns the AccessValue used by this specific filter
*
* @return an {@link AccessValue}
*/
public AccessValue getPrimaryAccessRule()
{
return fAccessRule;
}
public Set<INamespaceDefinition> getNamespaceSet(ICompilerProject project, ASScope scope)
{
return getNamespaceSet(project, scope, null);
}
Set<INamespaceDefinition> getNamespaceSet(ICompilerProject project, ASScope scope, String name)
{
if (fAccessRule != null)
{
// Check for user defined namespace access value.
if (!(fAccessRule instanceof AccessValue.SpecialAccessValue))
{
return Collections.singleton(fAccessRule.namespaceDef);
}
if (fAccessRule == AccessValue.ALL
// If we are looking for "ALL", but want to require imports, then build
// the normal namespace set
&& (fRequireImportsRule.searchAllNamespaces() || fSearchScopeRule.searchAllNamespaces()))
return ASScopeBase.allNamespacesSet;
Set<INamespaceDefinition> contextNamespaceSet = fContext.getNamespaceSet(project, name);
// NOTE: We do NOT need a LinkedHashSet here, because this
// namespace set is not used for code generation, just for
// code model compatibility.
Set<INamespaceDefinition> nsSet = new HashSet<INamespaceDefinition>(contextNamespaceSet.size());
nsSet.addAll(contextNamespaceSet);
if ((fAccessRule == AccessValue.PROTECTED) && (scope instanceof TypeScope))
{
TypeScope typeScope = (TypeScope)scope;
IDefinition typeIDefinition = typeScope.getDefinition();
if (typeIDefinition instanceof ClassDefinitionBase)
{
ClassDefinitionBase classDef = (ClassDefinitionBase)typeIDefinition;
nsSet.add(classDef.getProtectedNamespaceReference());
}
}
if (scope != null)
{
// Add all the interface namespaces, becuase the interface namespaces
// are mostly like "public" (i.e. they should be findable when we're searching
// for "public" stuff.
nsSet.addAll(getInterfaceNamespaceSets(project, scope));
}
if (shouldIncludeImplicitsAndConstructors())
nsSet.add(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
return nsSet;
}
return fContext.getNamespaceSet(project, name);
}
public Set<INamespaceDefinition> getNamespaceSetForName(ICompilerProject project, ASScope scope, String name)
{
return getNamespaceSet(project, scope, name);
}
/**
* Helper method to grab the interface namespaces when the DefinitionFilter
* is trying to find "public" definitions. Since the interface members get
* put in a special namespace, we have to add all those namespaces to the
* namespace set when the DefinitionFilter wants to find "public" because
* code model considers interface members as "public".
*
* @param project Project to use to resolve interfaces
* @param scope the scope we're looking in
* @return The interface namespace set.
*/
private Set<INamespaceDefinition> getInterfaceNamespaceSets(ICompilerProject project, ASScope scope)
{
Set<INamespaceDefinition> nsSet = new HashSet<INamespaceDefinition>();
while (scope != null)
{
ScopedDefinitionBase sdb = scope.getDefinition();
if (sdb instanceof InterfaceDefinition)
{
// If we find an interface, then just add it's interface namespace set
nsSet.addAll(((InterfaceDefinition)sdb).getInterfaceNamespaceSet(project));
break;
}
else if (sdb instanceof ClassDefinition)
{
// If we find a class, resolve it's interfaces, and then add all of their interface namespace
// sets
IInterfaceDefinition[] interfs = ((ClassDefinition)sdb).resolveImplementedInterfaces(project);
for (int i = 0, l = interfs.length; i < l; ++i)
{
if (interfs[i] != null)
nsSet.addAll(((InterfaceDefinition)interfs[i]).getInterfaceNamespaceSet(project));
}
break;
}
scope = scope.getContainingScope();
}
return nsSet;
}
/**
* Indicate whether to require that definitions be imported before including
* them in results
*
* @param requireImportsRule e.g. yes, no, only_functions_and_variables
*/
public void setRequireImports(RequireImportsValue requireImportsRule)
{
fRequireImportsRule = requireImportsRule;
}
/**
* Flag that determines if we should include classes marked with
* [ExcludeClass] metadata. This is only relevant when searching for members
* of a package Defaults to false
*
* @param includeExcludedClasses true if we want to include excluded
* metadata
*/
public void setIncludeExcludedClasses(boolean includeExcludedClasses)
{
fIncludeExcluded = includeExcludedClasses;
}
/**
* True if included classes should be included in this filter
*
* @return true if they are being included
*/
public boolean includeExcludedClasses()
{
return fIncludeExcluded != null && fIncludeExcluded == Boolean.TRUE;
}
@Deprecated
public void setFindOpenNamespacesInScope(boolean find)
{
// NOP We always know when to look for open namespaces.
}
/**
* Add a modifier to the list of modifiers that must be present in order for
* a definition to get through the filter
*
* @param modifier modifier (e.g. static, final, or override)
*/
public void setRequiredModifier(ASModifier modifier)
{
if (fRequiredAndExcludedModifiers == null)
fRequiredAndExcludedModifiers = new HashMap<ASModifier, Boolean>(4);
fRequiredAndExcludedModifiers.put(modifier, Boolean.TRUE);
}
/**
* Add a modifier to the list of modifiers that must NOT be present in order
* for a definition to get through the filter
*
* @param modifier modifier (e.g. static, final, or override)
*/
public void setExcludedModifier(ASModifier modifier)
{
if (fRequiredAndExcludedModifiers == null)
fRequiredAndExcludedModifiers = new HashMap<ASModifier, Boolean>(4);
fRequiredAndExcludedModifiers.put(modifier, Boolean.FALSE);
}
/**
* Removes a modifier from this list of this either required or excluded
*
* @param modifier the modifier
*/
public void removeRequiredOrExcludedModifier(ASModifier modifier)
{
if (fRequiredAndExcludedModifiers != null)
fRequiredAndExcludedModifiers.remove(modifier);
}
/**
* Determines if the modifier is required by our filter to indicate a match
*
* @param modifier the modifier we are looking for
* @return true if we require a modifer
*/
public boolean requiresModifier(ASModifier modifier)
{
if (fRequiredAndExcludedModifiers != null)
{
Object object = fRequiredAndExcludedModifiers.get(modifier);
if (object instanceof Boolean)
return ((Boolean)object).booleanValue();
}
return false;
}
/**
* Determines if the modifier is excluded by our filter to indicate a match
*
* @param modifier the modifier we are looking for
* @return true if we exclude a modifer
*/
public boolean excludesModifier(ASModifier modifier)
{
if (fRequiredAndExcludedModifiers != null)
{
Object object = fRequiredAndExcludedModifiers.get(modifier);
if (object instanceof Boolean)
return !((Boolean)object).booleanValue();
}
return false;
}
public void setIncludeImplicitsAndConstructors(boolean includeImplicitsAndConstructors)
{
fIncludeImplicitsAndConstructors = includeImplicitsAndConstructors;
}
public boolean shouldIncludeImplicitsAndConstructors()
{
return fIncludeImplicitsAndConstructors;
}
private boolean isClassMember(IDefinition definition)
{
return definition.getParent() instanceof IClassDefinition;
}
/**
* Determines if this filter is looking to match against a user-defined
* namespace
*
* @return true if this is not a built-in namespace
*/
public boolean isUserDefinedNamespace()
{
return (fAccessRule != null) && (fAccessRule != AccessValue.AS3) && (fAccessRule.namespaceDef != null);
}
/**
* Does the definition match the lists of required/excluded modifiers in
* this filter?
*
* @param definition definition to test
* @return true if this definition matches the modifier requirements
*/
public boolean matchesModifierRules(IDefinition definition)
{
if (fIncludeExcluded != null && !fIncludeExcluded)
{
boolean exclude = shouldBeExcluded(definition);
if (exclude)
return false;
}
if (fRequiredAndExcludedModifiers == null)
return true;
Iterator<ASModifier> modifiers = fRequiredAndExcludedModifiers.keySet().iterator();
while (modifiers.hasNext())
{
ASModifier modifier = modifiers.next();
// static only applies to class variables and class methods, so ignore
// all other cases
if (modifier.equals(ASModifier.STATIC))
{
//if we're a member, keep going unless we're a constructor of a class
if (!isClassMember(definition) ||
(isClassMember(definition) &&
definition instanceof IFunctionDefinition &&
((IFunctionDefinition)definition).isConstructor()))
{
continue;
}
}
// final only applies to classes and class methods, so ignore all other cases
else if (modifier.equals(ASModifier.FINAL))
{
if (!(definition instanceof IClassDefinition) &&
(!(definition instanceof IFunctionDefinition) || !isClassMember(definition)))
{
continue;
}
}
// override only applies to class methods, so ignore all other cases
else if (modifier.equals(ASModifier.OVERRIDE))
{
if (!(definition instanceof IFunctionDefinition) || !isClassMember(definition))
continue;
}
// native only applies to functions, so ignore all other cases
else if (modifier.equals(ASModifier.NATIVE))
{
if (!(definition instanceof IFunctionDefinition))
continue;
}
// dynamic only applies to classes, so ignore all other cases
else if (modifier.equals(ASModifier.DYNAMIC))
{
if (!(definition instanceof IClassDefinition))
continue;
}
if (fRequiredAndExcludedModifiers.get(modifier) == Boolean.TRUE)
{
if (!definition.hasModifier(modifier))
return false;
}
else
{
if (definition.hasModifier(modifier))
return false;
}
}
return true;
}
/**
* Should the definition be excluded from lookup results because it has
* [ExcludeClass] metadata. This method does not check the includeExcluded
* flag, it simply checks the definition for the presence of the metadata
*
* @param definition the definition to check
* @return true, if the definition should be excluded based on ExcludeClass
* metadata
*/
private static boolean shouldBeExcluded(IDefinition definition)
{
if (definition instanceof IClassDefinition)
{
// Skip any package members that are marked [ExcludeClass]
IMetaTag[] metaAttributes =
((IClassDefinition)definition).getMetaTagsByName(
IMetaAttributeConstants.ATTRIBUTE_EXCLUDECLASS);
if (metaAttributes.length > 0)
return true;
}
else if (definition instanceof IInterfaceDefinition)
{
IMetaTag[] tags = ((IInterfaceDefinition)definition).getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_EXCLUDECLASS);
if (tags.length > 0)
{
return true;
}
}
else if (definition instanceof IFunctionDefinition)
{
if (((IFunctionDefinition)definition).isConstructor())
{
IDefinition type = definition.getAncestorOfType(IClassDefinition.class);
if (type instanceof IClassDefinition)
{
IMetaTag[] metaAttributes =
((IClassDefinition)type).getMetaTagsByName(
IMetaAttributeConstants.ATTRIBUTE_EXCLUDECLASS);
if (metaAttributes.length > 0)
return true;
}
}
}
return false;
}
/**
* Does the definition match the classification portion of this filter?
*
* @param definition definition to test
* @return true iff this definition matches the classification rule
*/
public boolean matchesClassificationRule(IDefinition definition)
{
if (fClassificationRule == ClassificationValue.ALL)
{
return true;
}
else if (fClassificationRule == ClassificationValue.ALL_NO_ACCESSORS)
{
if (definition instanceof IGetterDefinition ||
definition instanceof ISetterDefinition)
{
return false;
}
return true;
}
else if (fClassificationRule == ClassificationValue.OBJECTS)
{
// don't exclude getters and setters
return !(definition instanceof IFunctionDefinition) ||
definition instanceof IVariableDefinition;
}
else if (fClassificationRule == ClassificationValue.ACCESSORS)
{
return definition instanceof IAccessorDefinition;
}
else if (fClassificationRule == ClassificationValue.VARIABLES_AND_FUNCTIONS)
{
return definition instanceof IVariableDefinition ||
definition instanceof IFunctionDefinition ||
definition instanceof IAccessorDefinition;
}
else if (fClassificationRule == ClassificationValue.GETTERS)
{
return definition instanceof IGetterDefinition;
}
else if (fClassificationRule == ClassificationValue.SETTERS)
{
return definition instanceof ISetterDefinition;
}
else if (fClassificationRule == ClassificationValue.VARIABLES)
{
return definition instanceof IVariableDefinition;
}
else if (fClassificationRule == ClassificationValue.META_TAG_TYPES)
{
return definition instanceof IMetadataDefinition;
}
else if (fClassificationRule == ClassificationValue.FUNCTIONS ||
fClassificationRule == ClassificationValue.VARIABLES_AND_FUNCTIONS)
{
//we might have an anonymous function, and if that's the case, if we have a variable, see if it's of type function
//if it is, return true
if (definition instanceof IVariableDefinition)
{
if (definition.getTypeAsDisplayString().equals(IASLanguageConstants.Function) ||
fClassificationRule == ClassificationValue.VARIABLES_AND_FUNCTIONS)
return true;
}
if (definition instanceof IFunctionDefinition)
{
return true;
}
// if we have a class, but we're filtering on functions, check for
// a constructor
if (definition instanceof ClassDefinition)
{
IFunctionDefinition ctor = ((ClassDefinition)definition).getConstructor();
return ctor != null;
}
return false;
}
else if (fClassificationRule == ClassificationValue.NAMESPACES)
{
return definition instanceof INamespaceDefinition;
}
else if (fClassificationRule == ClassificationValue.CLASSES)
{
return definition instanceof IClassDefinition;
}
else if (fClassificationRule == ClassificationValue.INTERFACES)
{
return definition instanceof IInterfaceDefinition;
}
else if (fClassificationRule == ClassificationValue.CLASSES_AND_INTERFACES)
{
return (definition instanceof IClassDefinition || definition instanceof IInterfaceDefinition);
}
else if (fClassificationRule == ClassificationValue.PACKAGES)
{
return definition instanceof IPackageDefinition;
}
else if (fClassificationRule == ClassificationValue.CLASSES_INTERFACES_AND_PACKAGES)
{
return (definition instanceof IClassDefinition ||
definition instanceof IInterfaceDefinition || definition instanceof IPackageDefinition);
}
else if (fClassificationRule == ClassificationValue.CLASSES_INTERFACES_AND_NAMESPACES)
{
return (definition instanceof IClassDefinition ||
definition instanceof IInterfaceDefinition || definition instanceof INamespaceDefinition);
}
else if (fClassificationRule == ClassificationValue.CLASSES_AND_PACKAGES)
{
return (definition instanceof IClassDefinition || definition instanceof IPackageDefinition);
}
else if (fClassificationRule == ClassificationValue.INTERFACES_AND_PACKAGES)
{
return (definition instanceof IInterfaceDefinition || definition instanceof IPackageDefinition);
}
else if (fClassificationRule == ClassificationValue.MEMBERS_AND_TYPES)
{
return definition instanceof IClassDefinition ||
definition instanceof IFunctionDefinition ||
definition instanceof IInterfaceDefinition ||
definition instanceof IVariableDefinition ||
definition instanceof INamespaceDefinition;
}
else if (fClassificationRule == ClassificationValue.TYPES_FUNCTIONS_AND_VARIABLES)
{
return definition instanceof IClassDefinition ||
(definition instanceof IFunctionDefinition &&
!((IFunctionDefinition)definition).isCastFunction() &&
!((IFunctionDefinition)definition).isConstructor() &&
!(definition instanceof IGetterDefinition) &&
!(definition instanceof ISetterDefinition)) ||
definition instanceof IVariableDefinition ||
definition instanceof IInterfaceDefinition;
}
else if (fClassificationRule == ClassificationValue.CLASSES_INTERFACES_AND_FUNCTIONS)
{
return definition instanceof IClassDefinition ||
definition instanceof IFunctionDefinition ||
definition instanceof IInterfaceDefinition;
}
else
{
// If we get here, the filter has been created with an invalid value.
return false;
}
}
/**
* Does this definition match the implicits/constructors part of this
* filter?
*
* @param definition node to test
* @param scope {@link ASScope} containing the original reference that we
* are resolving, can be null for project leve lookups.
* @return true iff this node matches the implicits/constructors rule
*/
public boolean matchesIncludeImplicitsAndConstructorsRule(IDefinition definition, ASScope scope)
{
boolean isImplicit = definition.isImplicit();
boolean isConstructor = (definition instanceof IFunctionDefinition) && ((IFunctionDefinition)definition).isConstructor();
if ((!isImplicit) && (!isConstructor))
return true;
if (fIncludeImplicitsAndConstructors)
{
// implicit definition and constructors that
// are inherited by the main name resolution code
// should always be filtered out.
// See http://bugs.adobe.com/jira/browse/CMP-1064 for
// an example.
// If this lookup does not originate from an ASScope, then
// the implicit definitions could not have been inherited so
// return true.
if (scope == null)
return true;
// if the definition does not have a containing
// scope it can not be inherited, so return true.
if (definition.getContainingScope() == null)
return true;
// if the definition associated with the definition is
// not a type definition ( like a class or interface ), then
// the definition can not be inherited, so return true.
if (!(definition.getContainingScope().getDefinition() instanceof TypeDefinitionBase))
return true;
// At this point the definition is known to be a member of a class
// or interface.
// We need to determine if the scope containing the
// definition is an instance scope that is on the scope
// chain.
ASScope currentScope = scope;
// If the scope chain starts at a type scope
// switch to the instance scope view of that type scope.
if (currentScope instanceof TypeScope)
{
currentScope = ((TypeScope)currentScope).getInstanceScope();
}
else
{
// Walk up the scope chain until we find a the instance scope for the enclosing
// type if there is one.
while ((currentScope != null) && (!(currentScope.getDefinition() instanceof TypeDefinitionBase)))
currentScope = currentScope.getContainingScope();
// There is no enclosing type scope on the scope chain, so we should
// filter out the definition ( by returning false ).
if (currentScope == null)
return false;
}
assert (currentScope.getDefinition() instanceof TypeDefinitionBase);
if (currentScope == definition.getContainingScope())
return true;
}
return false;
}
/**
* Get the require imports value
*/
public RequireImportsValue getRequireImportsValue()
{
return fRequireImportsRule;
}
/**
* Get the context node or null
*
* @return context node
*/
public IFilterContext getContext()
{
return fContext;
}
/**
* Does this filter indicate that we should look in base classes and
* interfaces?
*
* @return true iff we should search inherited scopes
*/
public boolean searchInheritedScopes()
{
return fSearchScopeRule == SearchScopeValue.ALL_SCOPES ||
fSearchScopeRule == SearchScopeValue.INHERITED_MEMBERS;
}
/**
* Does this filter indicate that we should look in enclosing blocks
* (functions, classes, packages, etc.)?
*
* @return true if we should search in containing scopes
*/
public boolean searchContainingScope()
{
return fSearchScopeRule == SearchScopeValue.ALL_SCOPES ||
fSearchScopeRule == SearchScopeValue.CONTAINING_SCOPES;
}
/**
* Create a member filter that's appropriate for the given object
* definition.
*
* @param object the object whose members we want
* @param objectDefinition the definition of the object type
* @param classificationRule type of data we're searching for (function,
* object, variable, whatever)
* @param project
* @param context the context node (to be used to determine the current
* imports and package)
* @return appropriate ASDefinitionFilter for member lookup
*/
static public ASDefinitionFilter createMemberFilter(IExpressionNode object,
IDefinition objectDefinition,
ClassificationValue classificationRule,
ICompilerProject project, IFilterContext context)
{
boolean requireStatic = true;
boolean excludeStatic = false;
AccessValue accessRule = ASDefinitionFilter.AccessValue.PUBLIC;
if (objectDefinition instanceof IVariableDefinition)
{
requireStatic = false;
excludeStatic = true;
ITypeDefinition variableType = objectDefinition.resolveType(project);
if (variableType instanceof IClassDefinition)
{
// If this member access expression is located inside a class definition, we should allow
// the user to see private members of variables of that class type and protected members
// of variables of base class types.
accessRule = adjustAccessRuleForContainingClass(object, (IClassDefinition)variableType, accessRule, project);
}
}
else if (objectDefinition instanceof IClassDefinition ||
objectDefinition instanceof IInterfaceDefinition)
{
if (!(object instanceof IdentifierNode) &&
!(object instanceof MemberAccessExpressionNode))
{
// If the thing on the left is just some random expression, it can't
// be a static.
requireStatic = false;
excludeStatic = true;
}
if (objectDefinition instanceof IClassDefinition)
{
accessRule = adjustAccessRuleForContainingClass(object, (IClassDefinition)objectDefinition, accessRule, project);
}
}
else if (objectDefinition instanceof IPackageDefinition ||
objectDefinition instanceof IFunctionDefinition)
{
requireStatic = false;
excludeStatic = false;
}
SearchScopeValue searchRule = ASDefinitionFilter.SearchScopeValue.INHERITED_MEMBERS;
ASDefinitionFilter filter = new ASDefinitionFilter(classificationRule, searchRule, accessRule, context);
if (requireStatic)
filter.setRequiredModifier(ASModifier.STATIC);
if (excludeStatic)
filter.setExcludedModifier(ASModifier.STATIC);
return filter;
}
/**
* Get the containing class. This particular version will even find the
* class if the current file is included by a host file and their scopes are
* temporarily connected (see Project.connectToIncluder).
*
* @param node node to test
* @return enclosing class (or null, if no such class can be found)
*/
static private IClassNode getContainingClass(IASNode node)
{
IClassNode containingClass = (IClassNode)node.getAncestorOfType(IClassNode.class);
while (node != null && containingClass == null)
{
FileNode containingFile = (FileNode)node.getAncestorOfType(FileNode.class);
IASScope scope = null;
if (containingFile != null)
scope = containingFile.getTemporaryEnclosingScope(new RecursionGuard());
node = containingFile != null ? (scope != null ? scope.getScopeNode() : null) : null;
containingClass = node != null ? containingClass = (IClassNode)node.getAncestorOfType(IClassNode.class) : null;
}
return containingClass;
}
/**
* If the member access is taking place inside a class definition, adjust
* the access rule to allow private members (if the object's type matches
* the class) or protected members (if the object's type matches a base
* class of the class).
*
* @param memberAccessExpression member access expression for which to
* generate the filter
* @param resolvedObjectType type of the object in the member access
* expression
* @param accessRule current access rule
* @return new access rule, combining the current value with whatever new
* members are allowed (see unionAccessRules)
*/
static private AccessValue adjustAccessRuleForContainingClass(IASNode memberAccessExpression, IClassDefinition resolvedObjectType, AccessValue accessRule, ICompilerProject project)
{
// If this member access expression is located inside a class definition, we should allow
// the user to see private members of variables of that class type and protected members
// of variables of base class types.
AccessValue newVal = accessRule;
IClassNode containingClass = getContainingClass(memberAccessExpression);
if (containingClass != null)
{
IClassDefinition containingClassDefinition = containingClass.getDefinition();
if (containingClassDefinition.getQualifiedName().equals(resolvedObjectType.getQualifiedName()))
{
newVal = AccessValue.PRIVATE;
}
else
{
IClassDefinition[] ancestors = containingClassDefinition.resolveAncestry(project);
for (IClassDefinition ancestor : ancestors)
{
if (ancestor.equals(resolvedObjectType))
{
newVal = AccessValue.PROTECTED;
break;
}
}
}
}
return newVal;
}
/**
* Create a filter that matches all functions in all scopes (including
* private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
@Deprecated
static public ASDefinitionFilter createAllFunctionsFilter(IASNode context)
{
return createAllFunctionsFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all functions in all scopes (including
* private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
@Deprecated
static public ASDefinitionFilter createAllFunctionsFilter(IDefinition context)
{
return createAllFunctionsFilter(new DefinitionFilterContext(context));
}
/**
* Create a filter that matches all functions in all scopes (including
* private members)
*
* @param context the context filter (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
static public ASDefinitionFilter createAllFunctionsFilter(IFilterContext context)
{
boolean contextIsStatic = false;
if (context != null)
contextIsStatic = context.isInStaticContext();
ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.FUNCTIONS, SearchScopeValue.ALL_SCOPES, AccessValue.PRIVATE, context);
if (contextIsStatic)
filter.setRequiredModifier(ASModifier.STATIC);
return filter;
}
/**
* Create a filter that matches all objects (variables, classes, interfaces,
* or packages) in all scopes (including private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
@Deprecated
static public ASDefinitionFilter createAllObjectsFilter(IASNode context)
{
return createAllObjectsFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all objects (variables, classes, interfaces,
* or packages) in all scopes (including private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
@Deprecated
static public ASDefinitionFilter createAllObjectsFilter(IDefinition context)
{
return createAllObjectsFilter(new DefinitionFilterContext(context));
}
/**
* Create a filter that matches all objects (variables, classes, interfaces,
* or packages) in all scopes (including private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
static public ASDefinitionFilter createAllObjectsFilter(IFilterContext context)
{
boolean contextIsStatic = false;
if (context != null)
contextIsStatic = context.isInStaticContext();
ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.OBJECTS, SearchScopeValue.ALL_SCOPES, AccessValue.PRIVATE, context);
if (contextIsStatic)
filter.setRequiredModifier(ASModifier.STATIC);
return filter;
}
/**
* Create a filter that matches all objects (variables, functions classes,
* interfaces, or packages) in all scopes (including private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
@Deprecated
static public ASDefinitionFilter createAllSymbolsFilter(IASNode context)
{
return createAllSymbolsFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all objects (variables, functions classes,
* interfaces, or packages) in all scopes (including private members)
*
* @param context the context node (to be used to determine the current
* imports and package and static/not)
* @return the filter
*/
static public ASDefinitionFilter createAllSymbolsFilter(IFilterContext context)
{
boolean contextIsStatic = false;
if (context != null)
contextIsStatic = context.isInStaticContext();
ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.ALL, SearchScopeValue.ALL_SCOPES, AccessValue.PRIVATE, context);
if (contextIsStatic)
filter.setRequiredModifier(ASModifier.STATIC);
filter.setIncludeExcludedClasses(true);
return filter;
}
/**
* Create a filter that matches all classes in all scopes
*
* @param context the filter context (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllClassesFilter(IFilterContext context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(ClassificationValue.CLASSES, SearchScopeValue.ALL_SCOPES, AccessValue.PUBLIC, context);
return definitionFilter;
}
/**
* Create a filter that matches all classes in all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllClassesFilter(IASNode context)
{
return createAllClassesFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all interfaces in all scopes
*
* @param context the filter context (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllInterfacesFilter(IFilterContext context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(ClassificationValue.INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.PUBLIC, context);
return definitionFilter;
}
/**
* Create a filter that matches all interfaces in all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllInterfacesFilter(IASNode context)
{
return createAllInterfacesFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all classes and all interfaces in all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllClassesAndInterfacesFilter(IASNode context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.PUBLIC, context);
return definitionFilter;
}
/**
* Create a filter that matches all classes, packages and all interfaces in
* all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllClassesInterfacesAndPackagesFilter(IASNode context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(ClassificationValue.CLASSES_INTERFACES_AND_PACKAGES, SearchScopeValue.ALL_SCOPES, AccessValue.PUBLIC, context);
return definitionFilter;
}
/**
* Create a filter that matches all namespaces in all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllNamespacesFilter(IASNode context)
{
return createAllNamespacesFilter(new NodeFilterContext(context));
}
/**
* Create a filter that matches all namespaces in all scopes
*
* @param context the context node (to be used to determine the current
* imports and package)
* @return the filter
*/
static public ASDefinitionFilter createAllNamespacesFilter(IFilterContext context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(ClassificationValue.NAMESPACES, SearchScopeValue.ALL_SCOPES, AccessValue.PUBLIC, context);
return definitionFilter;
}
/**
* Create a filter that matches immediate members of a package, class, or
* interface
*
* @param memberedDefinition definition whose members will be retrieved
* using this filter
* @param classificationValue classifications (CLASS, FUNCTION, ALL)
* @param includePrivateInternalAndNamespaceMembers if true, this filter
* will include private, protected, internal, custom namespaced and public
* members if false, this filter will only include public members
* @return definition filter for retrieving the members
*/
static public ASDefinitionFilter createImmediateMemberFilter(IDefinitionNode memberedDefinition, ClassificationValue classificationValue, boolean includePrivateInternalAndNamespaceMembers)
{
// If we're including private and internal members, then we should
// set the access value to PRIVATE and make sure that our context has
// the same package name as the class. If not, then we should set the
// access value to PUBLIC and make sure that our context does not have
// the same package name as the class.
AccessValue accessValue = AccessValue.PUBLIC;
IASNode context = null;
boolean dontRequireImports = false;
if (includePrivateInternalAndNamespaceMembers)
{
accessValue = AccessValue.ALL;
context = memberedDefinition;
// don't require imports, so that we won't do namespace filtering. This
// is a problem, as member methods within a different namespace weren't
// being included in the members
dontRequireImports = true;
}
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(classificationValue, SearchScopeValue.IMMEDIATE_MEMBERS, accessValue, context);
if (dontRequireImports)
definitionFilter.setRequireImports(RequireImportsValue.NO);
return definitionFilter;
}
static public ASDefinitionFilter createImmediateMemberFilter(IDefinition memberedDefinition, ClassificationValue classificationValue, boolean includePrivateInternalAndNamespaceMembers)
{
// If we're including private and internal members, then we should
// set the access value to PRIVATE and make sure that our context has
// the same package name as the class. If not, then we should set the
// access value to PUBLIC and make sure that our context does not have
// the same package name as the class.
AccessValue accessValue = AccessValue.PUBLIC;
IDefinition context = null;
boolean dontRequireImports = false;
if (includePrivateInternalAndNamespaceMembers)
{
accessValue = AccessValue.ALL;
context = memberedDefinition;
// don't require imports, so that we won't do namespace filtering. This
// is a problem, as member methods within a different namespace weren't
// being included in the members
dontRequireImports = true;
}
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(classificationValue, SearchScopeValue.IMMEDIATE_MEMBERS, accessValue, context);
if (dontRequireImports)
definitionFilter.setRequireImports(RequireImportsValue.NO);
return definitionFilter;
}
/**
* Create a filter that matches members that are included from a code
* fragment
*
* @param classificationValue classifications (CLASS, FUNCTION, ALL)
* @return definition filter for retrieving the members
*/
static public ASDefinitionFilter createIncludedSymbolsFilter(ClassificationValue classificationValue, IASNode context)
{
return ASDefinitionFilter.createIncludedSymbolsFilter(classificationValue, new NodeFilterContext(context));
}
/**
* Create a filter that matches members that are included from a code
* fragment
*
* @param classificationValue classifications (CLASS, FUNCTION, ALL)
* @return definition filter for retrieving the members
*/
static public ASDefinitionFilter createIncludedSymbolsFilter(ClassificationValue classificationValue, IFilterContext context)
{
ASDefinitionFilter definitionFilter = new ASDefinitionFilter(classificationValue, SearchScopeValue.IMMEDIATE_MEMBERS, AccessValue.INCLUDED, context);
return definitionFilter;
}
/**
* Create a filter that matches all members of a package, class, or
* interface (including inherited ones
*
* @param memberedDefinition definition whose members will be retrieved
* using this filter
* @param classificationValue classifications (CLASS, FUNCTION, ALL)
* @param includePrivateAndInternalMembers if true, this filter will include
* private, protected, internal, and public members if false, this filter
* will only include public members
* @return definition filter for retrieving the members
*/
static public ASDefinitionFilter createInheritedMemberFilter(IDefinitionNode memberedDefinition, ClassificationValue classificationValue, boolean includePrivateAndInternalMembers)
{
// If we're including private and internal members, then we should
// set the access value to PRIVATE and make sure that our context has
// the same package name as the class. If not, then we should set the
// access value to PUBLIC and make sure that our context does not have
// the same package name as the class.
AccessValue accessValue = AccessValue.PUBLIC;
IASNode context = null;
if (includePrivateAndInternalMembers)
{
accessValue = AccessValue.PRIVATE;
context = memberedDefinition;
}
return new ASDefinitionFilter(classificationValue, SearchScopeValue.INHERITED_MEMBERS, accessValue, context);
}
static public ASDefinitionFilter createInheritedMemberFilter(IDefinition memberedDefinition, ClassificationValue classificationValue, boolean includePrivateAndInternalMembers)
{
// If we're including private and internal members, then we should
// set the access value to PRIVATE and make sure that our context has
// the same package name as the class. If not, then we should set the
// access value to PUBLIC and make sure that our context does not have
// the same package name as the class.
AccessValue accessValue = AccessValue.PUBLIC;
IDefinition context = null;
if (includePrivateAndInternalMembers)
{
accessValue = AccessValue.PRIVATE;
context = memberedDefinition;
}
return new ASDefinitionFilter(classificationValue, SearchScopeValue.INHERITED_MEMBERS, accessValue, context);
}
/**
* Does this ASDefinitionFilter require the use of a different predicate to
* filter results found at the project level
*
* @return true if the definitions found at the project level should use a
* different filter from the rest of the lookup
*/
public boolean needsDifferentProjectPredicate()
{
return !includeExcludedClasses() || this.getProjectAccessRule() != null;
}
/**
* Generate a predicate that will apply the appropriate filtering for
* definitions found at the project level
*
* @param project the Project the lookup is occurring in
* @param scope the scope where we are performing the lookup
* @return a Predicate that will correctly filter definitions found at the
* project level.
*/
public Predicate<IDefinition> computeProjectPredicate(ICompilerProject project, ASScope scope)
{
Predicate<IDefinition> projectPredicate = null;
if (getProjectAccessRule() != null)
{
ASDefinitionFilter projectFilter = new ASDefinitionFilter(this);
projectFilter.setPrimaryAccessRule(this.getProjectAccessRule());
projectPredicate = projectFilter.computePredicate(project, scope);
}
else
{
projectPredicate = this.computePredicate(project, scope);
}
return projectPredicate;
}
/**
* Generate a predicate that will filter based on the various flags set on
* the ASDefinitionFilter.
*
* @param project the Project the lookup is occurring in
* @param scope the scope where we are performing the lookup
* @return a Predicate that can be used to filter lookup results according
* to the settings of the ASDefinitionFilter
*/
public Predicate<IDefinition> computePredicate(ICompilerProject project, ASScope scope)
{
Predicate<IDefinition> pred = null;
pred = new FilterPredicate(project, scope, this);
if (fAccessRule instanceof AccessValue.SpecialAccessValue)
{
Predicate<IDefinition> accessValPredicate = computeAccessValuePredicate(project, scope);
if (accessValPredicate != null)
{
pred = Predicates.and(pred, accessValPredicate);
}
}
if (!includeExcludedClasses())
{
Predicate<IDefinition> excludePred = new ExcludedPredicate(scope);
pred = Predicates.and(pred, excludePred);
}
return pred;
}
/**
* Generate a predicate based on the AccessValue of the ASDefinitionFilter.
* This predicate will do the right thing for private, protected, internal,
* or public access values.
*/
public Predicate<IDefinition> computeAccessValuePredicate(ICompilerProject project, ASScope scope)
{
Predicate<IDefinition> pred = null;
if (fAccessRule == AccessValue.ALL)
{
pred = null;
}
else if (fAccessRule == AccessValue.PRIVATE)
{
pred = new PrivateAccessValuePredicate(project, fContext.getNamespaceSet(project));
}
else
{
if (fAccessRule == AccessValue.PUBLIC)
{
pred = new PublicAccessValuePredicate();
}
else if (fAccessRule == AccessValue.INTERNAL)
{
pred = new InternalAccessValuePredicate();
}
else if (fAccessRule == AccessValue.PROTECTED)
{
pred = new ProtectedAccessValuePredicate();
}
// CM expects these rules to match the public/internal/protected namespaces,
// but to also match stuff in the open namespaces wherever the lookup was occurring
if (pred != null)
pred = Predicates.or(pred, new NamespaceSetPredicate(project, fContext.getNamespaceSet(project)));
}
return pred;
}
/**
* Predicate used for filters set up with a "PUBLIC" access value.
*/
private static class PublicAccessValuePredicate implements Predicate<IDefinition>
{
@Override
public boolean apply(IDefinition d)
{
INamespaceReference ns = d.getNamespaceReference();
if (ns.isLanguageNamespace())
{
boolean match = ns instanceof INamespaceDefinition.IPublicNamespaceDefinition ||
ns instanceof INamespaceDefinition.IFilePrivateNamespaceDefinition ||
ns instanceof INamespaceDefinition.IInterfaceNamespaceDefinition;
return match;
}
// user defined namespaces will be handled by another predicate
return false;
}
}
/**
* Predicate used for filters set up with a "INTERNAL" access value.
*/
private static class InternalAccessValuePredicate implements Predicate<IDefinition>
{
@Override
public boolean apply(IDefinition d)
{
INamespaceReference ns = d.getNamespaceReference();
if (ns.isLanguageNamespace())
{
boolean match = ns.isPublicOrInternalNamespace() ||
ns instanceof INamespaceDefinition.IFilePrivateNamespaceDefinition ||
ns instanceof INamespaceDefinition.IInterfaceNamespaceDefinition;
return match;
}
// user defined namespaces will be handled by another predicate
return false;
}
}
/**
* Predicate used for filters set up with a "PROTECTED" access value.
*/
private static class ProtectedAccessValuePredicate implements Predicate<IDefinition>
{
@Override
public boolean apply(IDefinition d)
{
INamespaceReference nsRef = d.getNamespaceReference();
if (nsRef.isLanguageNamespace())
{
boolean match = nsRef.isPublicOrInternalNamespace() ||
nsRef instanceof INamespaceDefinition.IProtectedNamespaceDefinition ||
nsRef instanceof INamespaceDefinition.IFilePrivateNamespaceDefinition ||
nsRef instanceof INamespaceDefinition.IInterfaceNamespaceDefinition;
return match;
}
// user defined namespaces will be handled by another predicate
return false;
}
}
/**
* This implements a very simple namespace set predicate for use in
* combination with the various AccessValue Predicates. It does not do the
* right thing with respect to protected namespaces - however, when it is
* used as intended with the AccessValue predicates this is not an issue as
* CM clients construct filters with "PROTECTED" AccessValues only where
* they want to find all protecteds, and this does the right thing in that
* case.
*/
private static class NamespaceSetPredicate implements Predicate<IDefinition>
{
protected Set<INamespaceDefinition> nsSet;
protected ICompilerProject project;
NamespaceSetPredicate(ICompilerProject project, Set<INamespaceDefinition> set)
{
nsSet = set;
this.project = project;
}
@Override
public boolean apply(IDefinition d)
{
INamespaceDefinition namespace = d.resolveNamespace(project);
if (nsSet.contains(namespace))
return true;
return false;
}
}
/**
* Predicate used for filters set up with a "PRIVATE" access value. This
* will find the private members of the containing class, but no private
* members from base classes, which is what CM clients expect.
*/
private static class PrivateAccessValuePredicate extends NamespaceSetPredicate
{
PrivateAccessValuePredicate(ICompilerProject project, Set<INamespaceDefinition> set)
{
super(project, set);
}
@Override
public boolean apply(IDefinition d)
{
INamespaceReference nsRef = d.getNamespaceReference();
if (nsRef instanceof INamespaceDefinition.IPrivateNamespaceDefinition || !nsRef.isLanguageNamespace())
{
// If it's a private namespace, or a user defined namespace, then check if it is in our namespace set
// FB says it wants private properties, but it really means it only wants private properties
// that are in the open namespace set
return super.apply(d);
}
// If it's any other kind of language namespace, then CM wants it included
return true;
}
}
/**
* Predicate to filter out classes and interfaces with [ExcludeClass]
* metadata
*/
private static class ExcludedPredicate implements Predicate<IDefinition>
{
private String filePath;
/**
* @param scope
*/
public ExcludedPredicate(ASScope scope)
{
if (scope != null)
{
ASScope fileScope = scope.getFileScope();
if (fileScope instanceof ASFileScope)
{
filePath = ((ASFileScope)fileScope).getContainingPath();
}
}
}
@Override
public boolean apply(IDefinition arg0)
{
// check whether the definition is in the same file as the scope
// if the files are same, then apply filter only in case of
// mxml files and definitions present directly under file
// scope (Component tag in mxml)
IASScope containingScope = arg0.getContainingScope();
if (containingScope != null && containingScope instanceof ASScope)
{
IASScope fileScope = ((ASScope)containingScope).getFileScope();
String defPath = ""; //$NON-NLS-1$
if (fileScope instanceof ASFileScope)
{
defPath = ((ASFileScope)fileScope).getContainingPath();
}
if (filePath != null && defPath.equals(filePath))
{
// same file, apply filter only in case of mxml
if (containingScope instanceof MXMLFileScope)
return !ASDefinitionFilter.shouldBeExcluded(arg0);
return true;
}
}
if (ASDefinitionFilter.shouldBeExcluded(arg0))
return false;
return true;
}
}
private ICompilerProject findPropertForContainingPathAndScope(final String containingPath, final IASScope scope, final Iterable<WeakReference<ICompilationUnit>> units)
{
for (WeakReference<ICompilationUnit> unitRef : units)
{
ICompilationUnit unit = unitRef.get();
if (unit != null)
{
try
{
IASScope[] scopes = unit.getFileScopeRequest().get().getScopes();
for (IASScope cuScope : scopes)
{
if (scope == cuScope)
return unit.getProject();
}
}
catch (InterruptedException e)
{
}
}
}
return null;
}
private ICompilerProject findProjectForScope(IASScope scope)
{
while ((scope != null) && (!(scope instanceof ASFileScope)))
{
// Check for TypeScope's since the TypeScope
// might be a scope for a Vector.
// Vector definitions ( aka AppliedVectorDefinition's ) have a reference
// to the project, but are not contained by a file scope.
if (scope instanceof TypeScope || scope instanceof ScopeView)
{
ASScope typeScope = (ASScope)scope;
ITypeDefinition typeDefinition = (ITypeDefinition)typeScope.getDefinition();
if (typeDefinition instanceof AppliedVectorDefinition)
return ((AppliedVectorDefinition)typeDefinition).getProject();
}
scope = scope.getContainingScope();
}
if (scope == null)
return null;
final ASFileScope fileScope = (ASFileScope)scope;
final Workspace workspace = (Workspace)fileScope.getWorkspace();
final String containingPath = fileScope.getContainingPath();
ICompilerProject result = findPropertForContainingPathAndScope(containingPath, scope, workspace.getInvisibleCompilationUnits(containingPath));
if (result != null)
return result;
result = findPropertForContainingPathAndScope(containingPath, scope, workspace.getCompilationUnits(containingPath));
return result;
}
private ICompilerProject findProjectForDefinition(IDefinition def)
{
// first check for cases where we can get the project directly,
// rather than getting the scope
if (def instanceof AppliedVectorDefinition)
return ((AppliedVectorDefinition)def).getProject();
// If we need to, get the scope and find the project from that.
// Warning: this can be slow.
return findProjectForScope(def.getContainingScope());
}
}