/*
*
* 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.metadata;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.metadata.IMetaTag;
import org.apache.flex.compiler.filespecs.IFileSpecification;
import org.apache.flex.compiler.internal.definitions.metadata.MetaTag;
import org.apache.flex.compiler.internal.scopes.ASScope;
import org.apache.flex.compiler.internal.semantics.PostProcessStep;
import org.apache.flex.compiler.internal.tree.as.BaseDefinitionNode;
import org.apache.flex.compiler.internal.tree.as.BlockNode;
import org.apache.flex.compiler.internal.tree.as.ClassNode;
import org.apache.flex.compiler.internal.tree.as.ContainerNode;
import org.apache.flex.compiler.internal.tree.as.MemberedNode;
import org.apache.flex.compiler.internal.tree.as.NodeBase;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.tree.ASTNodeID;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IDefinitionNode;
import org.apache.flex.compiler.tree.as.IScopedNode;
import org.apache.flex.compiler.tree.metadata.IMetaTagNode;
import org.apache.flex.compiler.tree.metadata.IMetaTagsNode;
public class MetaTagsNode extends ContainerNode implements IMetaTagsNode
{
private IDefinitionNode decoratedNode;
/**
* Default constructor
*/
public MetaTagsNode()
{
super();
setContainerType(ContainerType.BRACKETS);
}
public void setDecorationTarget(IDefinitionNode node)
{
decoratedNode = node;
}
public IDefinitionNode getDecoratedDefinition()
{
if (decoratedNode == null)
{
IASNode parent = getParent();
if (parent instanceof BlockNode)
{
IASNode node = parent.getParent();
if (node instanceof MemberedNode)
{
BlockNode contents = ((MemberedNode)node).getScopedNode();
int childCount = contents.getChildCount();
boolean found = false;
for (int i = 0; i < childCount; i++)
{
IASNode child = contents.getChild(i);
if (found)
{
if (child instanceof IDefinitionNode)
{
decoratedNode = (IDefinitionNode)child;
if (child instanceof ClassNode)
{
analyze(EnumSet.of(PostProcessStep.POPULATE_SCOPE), ((ClassNode)child).getScopedNode().getASScope(), new ArrayList<ICompilerProblem>(0));
}
if (child instanceof BaseDefinitionNode)
{
((BaseDefinitionNode)child).setMetaTags(this);
}
break;
}
}
else
{
if (child == this)
{
found = true;
}
}
}
//we're still null, so let's loop through backwards to see if we can find the node we are decorating
//this could happen in MXML land when the mx:MetaData block comes after a script/event block
if (decoratedNode == null && found)
{
for (int i = childCount - 1; i >= 0; i--)
{
IASNode child = contents.getChild(i);
if (child instanceof IDefinitionNode)
{
decoratedNode = (IDefinitionNode)child;
if (child instanceof ClassNode)
{
analyze(EnumSet.of(PostProcessStep.POPULATE_SCOPE), ((ClassNode)child).getScopedNode().getASScope(), new ArrayList<ICompilerProblem>(0));
}
if (child instanceof BaseDefinitionNode)
{
((BaseDefinitionNode)child).setMetaTags(this);
}
break;
}
}
}
}
}
else if (parent instanceof IDefinitionNode)
{
decoratedNode = (IDefinitionNode)parent;
}
}
return decoratedNode;
}
@Override
protected int getInitialChildCount()
{
return 0;
}
public void addTag(MetaTagNode node)
{
if (node != null)
addChild(node);
}
@Override
public void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems)
{
// An "unbound" metadata will have "null" scope because it doesn't
// have a bound definition.
if (scope == null)
return;
final IScopedNode scopeNode = scope.getScopeNode();
if (scopeNode != null &&
scopeNode.getParent() != null &&
scopeNode.getParent().equals(getDecoratedDefinition()))
{
super.analyze(set, scope, problems);
}
}
@Override
public void addItem(NodeBase child)
{
if (child instanceof IMetaTagNode)
super.addChildInOrder(child, true);
}
@Override
public void addChild(NodeBase child)
{
if (child instanceof IMetaTagNode)
super.addChildInOrder(child, true);
}
@Override
protected void addChildInOrder(NodeBase newChild, boolean fillInOffsets)
{
if (newChild instanceof IMetaTagNode)
super.addChildInOrder(newChild, fillInOffsets);
}
@Override
protected boolean canContinueContainmentSearch(IASNode containingNode, IASNode currentNode, int childOffset, boolean offsetStillValid)
{
//we need to establish that it's safe to return the containing node here
//especially, if the last node was a metadata node
//check to see if we're metadata, and if we are generated from MXML
//if we are generated from mx:Metadata, don't let us return the block since it's possible
//that the metadata can span the entire contents of the class
// <mx:Metadata><mx:Script><mx:MetaData>
if (offsetStillValid)
{
if (isTransparent() &&
containingNode instanceof BlockNode && containingNode == currentNode)
{
return true;
}
}
else
{
if (isTransparent())
return true;
}
return false;
}
/**
* Remove a meta attribute to the set. This is not commonly done... usually
* this information is static after it is parsed. However, the ABCEvaluator
* uses it to remove the __go_to_definition_help metadata after it has
* gleaned the start offset from it.
*
* @param node node being removed
*/
public void removeAttribute(MetaTagNode node)
{
int childCount = getChildCount();
for (int i = 0; i < childCount; i++)
{
if (getChild(i) == node)
removeChild(node);
}
}
/**
* Determine if this set of attributes is empty
*
* @return true if there are no attributes
*/
public boolean isEmpty()
{
return getChildCount() <= 0;
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof MetaTagsNode))
return false;
MetaTagsNode other = (MetaTagsNode)obj;
int attributesCount = getChildCount();
int otherAttributesCount = other.getChildCount();
if (other.getAbsoluteStart() != this.getAbsoluteStart())
return false;
if (other.getAbsoluteEnd() != this.getAbsoluteEnd())
return false;
if (attributesCount != otherAttributesCount)
return false;
if (attributesCount == 0)
return true;
//enforce order
for (int i = 0; i < attributesCount; i++)
{
IASNode attribute = getChild(i);
IASNode otherAttribute = other.getChild(i);
if (!attribute.equals(otherAttribute))
return false;
}
return true;
}
@Override
public IMetaTagNode[] getAllTags()
{
int childCount = getChildCount();
IMetaTagNode[] tags = new IMetaTagNode[childCount];
for (int i = 0; i < childCount; i++)
{
tags[i] = (IMetaTagNode)getChild(i);
}
return tags;
}
@Override
public IMetaTagNode[] getTagsByName(String name)
{
int size = getChildCount();
ArrayList<IMetaTagNode> list = new ArrayList<IMetaTagNode>(size);
for (int i = 0; i < size; i++)
{
IASNode child = getChild(i);
if (child instanceof IMetaTagNode && ((IMetaTagNode)child).getTagName().compareTo(name) == 0)
{
list.add((IMetaTagNode)child);
}
}
return list.toArray(new IMetaTagNode[0]);
}
@Override
public boolean hasTagByName(String name)
{
IMetaTagNode[] byName = getTagsByName(name);
return byName != null && byName.length > 0;
}
@Override
public IMetaTagNode getTagByName(String name)
{
int size = getChildCount();
for (int i = 0; i < size; i++)
{
if (((IMetaTagNode)getChild(i)).getTagName().compareTo(name) == 0)
{
return (IMetaTagNode)getChild(i);
}
}
return null;
}
public IMetaTag[] buildMetaTags(IFileSpecification containingFileSpec, IDefinition definition)
{
assert containingFileSpec != null;
assert definition != null;
IMetaTagNode[] metaTagNodes = getAllTags();
int n = metaTagNodes.length;
IMetaTag[] metaTags = new MetaTag[n];
for (int i = 0; i < n; i++)
{
metaTags[i] = ((MetaTagNode)metaTagNodes[i]).buildMetaTag(containingFileSpec, definition);
}
return metaTags;
}
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.MetaTagsID;
}
}