blob: 834f7f513eae10ec3ec8bd38c8ca6e4b0bb45e90 [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.royale.compiler.internal.tree.mxml;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import org.apache.royale.compiler.internal.parsing.ISourceFragment;
import org.apache.royale.compiler.internal.parsing.SourceFragment;
import org.apache.royale.compiler.internal.parsing.SourceFragmentsReader;
import org.apache.royale.compiler.internal.parsing.as.ASParser;
import org.apache.royale.compiler.internal.parsing.as.IRepairingTokenBuffer;
import org.apache.royale.compiler.internal.parsing.as.StreamingASTokenizer;
import org.apache.royale.compiler.internal.parsing.as.StreamingTokenBuffer;
import org.apache.royale.compiler.internal.semantics.PostProcessStep;
import org.apache.royale.compiler.internal.tree.as.FileNode;
import org.apache.royale.compiler.internal.tree.as.NodeBase;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.mxml.IMXMLLanguageConstants;
import org.apache.royale.compiler.mxml.IMXMLTagAttributeData;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IClassNode;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.tree.as.IIdentifierNode;
import org.apache.royale.compiler.tree.mxml.IMXMLImplementsNode;
/**
* Implementation of the {@code IMXMLImplementsNode} interface.
*/
class MXMLImplementsNode extends MXMLNodeBase implements IMXMLImplementsNode
{
// The {@code interfaceNodes} field is initialized to this empty array.
private static final IIdentifierNode[] NO_INTERFACE_NODES = new IIdentifierNode[0];
// These strings are used to turn the MXML attribute
// implements="com.whatever.IFoo, com.whatever.IBar"
// into the class declaration
// internal class C implements com.whatever.IFoo, com.whatever.IBar {}
// which can be parsed by the AS parser.
// TODO It would be preferable to be able to parse
// just the attribute value without turning it into a fake class declaration,
// but currently the AS parser doesn't have a production for the
// interface list or even the implements clause.
private static final String STRING_TO_PREPEND = "internal class C implements ";
private static final String STRING_TO_APPEND = " {}";
/**
* Constructor
*
* @param parent The parent node of this node, or <code>null</code> if there
* is no parent.
*/
MXMLImplementsNode(NodeBase parent)
{
super(parent);
}
/**
* Storage for the child nodes representing the implemented interfaces.
*/
private IIdentifierNode[] interfaceNodes = NO_INTERFACE_NODES;
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.MXMLImplementsID;
}
@Override
public String getName()
{
return IMXMLLanguageConstants.ATTRIBUTE_IMPLEMENTS;
}
@Override
public int getChildCount()
{
return interfaceNodes.length;
}
@Override
public IASNode getChild(int i)
{
return interfaceNodes[i];
}
@Override
public IIdentifierNode[] getInterfaceNodes()
{
return interfaceNodes;
}
/**
* Initializes this {@code MXMLImplementNode} from the value of the
* <code>implements</code> attribute.
*/
protected void initializeFromAttribute(MXMLTreeBuilder builder,
IMXMLTagAttributeData attribute)
{
setLocation(attribute);
adjustOffsets(builder);
Workspace workspace = builder.getWorkspace();
Collection<ICompilerProblem> problems = builder.getProblems();
// Fragmentize the attribute value, to deal with XML entities.
ISourceFragment[] valueFragments = attribute.getValueFragments(problems);
int n = valueFragments.length;
// Turn the attribute value into fragments for a fake class declaration
// that can be parsed into a {@code FileNode} by the AS parser.
ISourceFragment[] fragments = new ISourceFragment[n + 2];
fragments[0] = new SourceFragment(STRING_TO_PREPEND, STRING_TO_PREPEND, 0, 0, 0);
for (int i = 0; i < n; i++)
{
fragments[i + 1] = valueFragments[i];
}
fragments[n + 1] = new SourceFragment(STRING_TO_APPEND, STRING_TO_APPEND, 0, 0, 0);
// Parse the fake class declaration into a {@code FileNode}.
SourceFragmentsReader reader = new SourceFragmentsReader(attribute.getSourcePath(), fragments);
StreamingASTokenizer tokenizer = new StreamingASTokenizer();
tokenizer.setReader(reader);
IRepairingTokenBuffer buffer = new StreamingTokenBuffer(tokenizer);
ASParser parser = new ASParser(workspace, buffer);
FileNode fileNode = new FileNode(builder.getFileSpecificationGetter());
parser.parseFile(fileNode, EnumSet.of(PostProcessStep.CALCULATE_OFFSETS));
problems.addAll(tokenizer.getTokenizationProblems());
problems.addAll(parser.getSyntaxProblems());
// Find the nodes representing the interfaces inside the {@code FileNode}.
List<IIdentifierNode> interfaceNodeList = new ArrayList<IIdentifierNode>();
if (fileNode.getChildCount() == 1 && fileNode.getChild(0) instanceof IClassNode)
{
IClassNode classNode = (IClassNode)fileNode.getChild(0);
for (IExpressionNode interfaceNode : classNode.getImplementedInterfaceNodes())
{
if (interfaceNode instanceof IIdentifierNode)
{
interfaceNodeList.add((IIdentifierNode)interfaceNode);
// Reparent them onto this {@code MXMLImplementsNode}.
((NodeBase)interfaceNode).setParent(this);
}
}
}
// Set the nodes representing the interfaces
// as the children of this {@code MXMLImplementsNode}.
interfaceNodes = interfaceNodeList.toArray(new IIdentifierNode[0]);
}
}