/*
*
* 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.tree.as;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import org.apache.flex.compiler.common.ASModifier;
import org.apache.flex.compiler.internal.scopes.ASScope;
import org.apache.flex.compiler.internal.scopes.TypeScope;
import org.apache.flex.compiler.internal.semantics.PostProcessStep;
import org.apache.flex.compiler.internal.tree.as.parts.FunctionContentsPart;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.scopes.IASScope;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IImportNode;
import org.apache.flex.compiler.tree.as.IScopedNode;
/**
* A BlockNode that is provided with a scope that collections definitions.
*/
public class ScopedBlockNode extends BlockNode implements IScopedNode
{
/**
* Constructor.
*/
public ScopedBlockNode()
{
this(true);
}
/**
* Constructor.
* <p>
* This variant is used by {@link FunctionContentsPart}.
*
* @param compressChildrenOnNormalization False if
* {@code optimizeChildren(Object)} should be skipped during
* {@code normalize(boolean)}.
*/
public ScopedBlockNode(boolean compressChildrenOnNormalization)
{
super();
this.compressChildrenOnNormalization = compressChildrenOnNormalization;
}
private final boolean compressChildrenOnNormalization;
/**
* The scope associated with this block
*/
protected ASScope scope;
//
// NodeBase overrides
//
@Override
public ASScope getASScope()
{
return scope;
}
@Override
public IASScope getScope()
{
return scope;
}
@Override
// TODO Remove unnecessary override.
public Collection<ICompilerProblem> runPostProcess(EnumSet<PostProcessStep> set, ASScope containingScope)
{
return super.runPostProcess(set, containingScope);
}
@Override
protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems)
{
if (scope instanceof TypeScope)
{
// TypeScopes need to pass the class scope, or instance scope to children based on if
// the children are static or not so that the children will be set up with the correct scope chain
TypeScope typeScope = (TypeScope)scope;
ASScope classScope = typeScope.getStaticScope();
ASScope instanceScope = typeScope.getInstanceScope();
// Populate this scope with definitions found among the relevant descendants
List <IASNode> children = getDescendantStatements(this);
for (IASNode child : children)
{
if (child instanceof NodeBase)
{
if (child.getParent() == null)
((NodeBase)child).setParent(this);
((NodeBase)child).analyze(set, childInStaticScope(child) ? classScope : instanceScope, problems);
}
}
if (scope != null)
scope.compact();
}
else
{
super.analyze(set, scope, problems);
}
if (scope != null)
scope.compact();
}
/**
* looks at children, but skips over non-statement nodes to get the closest descendant nodes that
* represent statements.
*/
private static List<IASNode> getDescendantStatements(IASNode parent)
{
ArrayList<IASNode> ret = new ArrayList<IASNode>();
int childrenSize = parent.getChildCount();
for (int i = 0; i < childrenSize; i++)
{
IASNode child = parent.getChild(i);
if (child instanceof ConfigConditionBlockNode)
{
ret.addAll( getDescendantStatements(child));
}
else
{
ret.add(child);
}
}
return ret;
}
//
// TreeNode overrides
//
@Override
protected final void optimizeChildren(Object newChildren)
{
// If this node is the content node of a {@code FunctionNode}, do not
// compress the children field into an immutable array. The children will be
// populated later when the body is rebuilt.
if (compressChildrenOnNormalization)
super.optimizeChildren(newChildren);
}
//
// IScopedNode implementations
//
@Override
public void getAllImportNodes(Collection<IImportNode> importNodes)
{
// Collect the import nodes that are descendants of this node
// but which are not within other scoped nodes.
collectImportNodes(importNodes);
// Recurse up the chain of scoped nodes to collect import nodes
// from higher scopes.
IScopedNode parent = (IScopedNode)getAncestorOfType(IScopedNode.class);
// if parent is package node, don't fetch imports from the file node
// as file scope imports are not applicable in this scope
if (parent != null )
{
if( !(getParent() instanceof PackageNode) )
parent.getAllImportNodes(importNodes);
// If we're a package, then just grab the implicit imports from the FileNode
else if (parent instanceof FileNode )
((FileNode)parent).collectImplicitImportNodes(importNodes);
}
}
@Override
public void getAllImports(Collection<String> imports)
{
ArrayList<IImportNode> importNodes = new ArrayList<IImportNode>();
getAllImportNodes(importNodes);
for (IImportNode importNode : importNodes)
{
imports.add(importNode.getImportName());
}
}
//
// Other methods
//
void reconnectScope(ASScope scope)
{
this.scope = scope;
scope.reconnectScopeNode(this);
}
public Collection<ICompilerProblem> runPostProcess(EnumSet<PostProcessStep> set)
{
return runPostProcess(set, null);
}
/**
* Attaches this node to the scope that has been created for it.
*
* @param scope The {@link ASScope} for this node.
*/
public void setScope(ASScope scope)
{
this.scope = scope;
}
private boolean childInStaticScope(IASNode child)
{
// If the child is a defininition then static-ness is determined by the presence, or
// lack of, the "static" modifier.
if (child instanceof BaseDefinitionNode)
return ((BaseDefinitionNode)child).hasModifier(ASModifier.STATIC)
// Namespaces are always static
|| child instanceof NamespaceNode;
// The child is not a definition, so it is loose code in the class body, so it is
// static
return true;
}
}