blob: ee5051f1ecc56a0a851a691b83e216e52680d5ee [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.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.MXMLDualContentProblem;
import org.apache.royale.compiler.problems.UnexpectedExceptionProblem;
import org.apache.royale.compiler.config.CompilerDiagnosticsConstants;
import org.apache.royale.compiler.filespecs.IFileSpecification;
import org.apache.royale.compiler.internal.mxml.MXMLDialect;
import org.apache.royale.compiler.internal.parsing.as.ASParser;
import org.apache.royale.compiler.internal.parsing.as.IncludeHandler;
import org.apache.royale.compiler.internal.parsing.as.OffsetLookup;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.MXMLFileScope;
import org.apache.royale.compiler.internal.semantics.PostProcessStep;
import org.apache.royale.compiler.internal.tree.as.NodeBase;
import org.apache.royale.compiler.internal.tree.as.ScopedBlockNode;
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.mxml.IMXMLTagData;
import org.apache.royale.compiler.mxml.IMXMLTextData;
import org.apache.royale.compiler.mxml.IMXMLUnitData;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.mxml.IMXMLScriptNode;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.*;
class MXMLScriptNode extends MXMLNodeBase implements IMXMLScriptNode
{
/**
* Constructor
*
* @param parent The parent node of this node, or <code>null</code> if there
* is no parent.
*/
MXMLScriptNode(NodeBase parent)
{
super(parent);
}
/**
* The ActionScript nodes representing the code inside the script tag.
*/
private IASNode[] asNodes;
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.MXMLScriptID;
}
@Override
public IASNode getChild(int i)
{
return asNodes != null ? asNodes[i] : null;
}
@Override
public int getChildCount()
{
return asNodes != null ? asNodes.length : 0;
}
@Override
public String getName()
{
return IMXMLLanguageConstants.SCRIPT;
}
@Override
public IASNode[] getASNodes()
{
if (asNodes == null)
asNodes = new MXMLScriptNode[0];
return asNodes;
}
@Override
protected MXMLNodeInfo createNodeInfo(MXMLTreeBuilder builder)
{
return new MXMLNodeInfo(builder);
}
@Override
protected void processTagSpecificAttribute(MXMLTreeBuilder builder, IMXMLTagData tag,
IMXMLTagAttributeData attribute,
MXMLNodeInfo info)
{
if (attribute.isSpecialAttribute(ATTRIBUTE_SOURCE))
{
MXMLClassDefinitionNode classNode = getContainingClassNode();
ASScope classScope = (ASScope)classNode.getClassDefinition().getContainedScope();
// Resolve the attribute value to a normalized path.
// Doing so makes this compilation unit dependent on that file.
String sourcePath = resolveSourceAttributePath(builder, attribute, info);
if (sourcePath != null)
{
RoyaleProject project = builder.getProject();
Workspace workspace = builder.getWorkspace();
Collection<ICompilerProblem> problems = builder.getProblems();
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("MXMLScriptNode waiting for lock in processTagSpecificAttribute");
IFileSpecification sourceFileSpec = workspace.getFileSpecification(sourcePath);
if ((CompilerDiagnosticsConstants.diagnostics & CompilerDiagnosticsConstants.WORKSPACE) == CompilerDiagnosticsConstants.WORKSPACE)
System.out.println("MXMLScriptNode done with lock in processTagSpecificAttribute");
String scriptText = builder.readExternalFile(attribute, sourcePath);
if (scriptText != null)
{
final IncludeHandler includeHandler = new IncludeHandler(builder.getFileSpecificationGetter());
includeHandler.setProjectAndCompilationUnit(project, builder.getCompilationUnit());
includeHandler.enterFile(sourcePath);
final MXMLFileScope fileScope = builder.getFileScope();
final OffsetLookup offsetLookup = fileScope.getOffsetLookup();
assert offsetLookup != null : "Expected OffsetLookup on FileScope.";
final int[] absoluteOffset = offsetLookup.getAbsoluteOffset(sourcePath, 0);
final ScopedBlockNode fragment = ASParser.parseFragment2(
scriptText,
sourceFileSpec.getPath(),
absoluteOffset[0],
0,
0,
problems,
workspace,
builder.getFileNode(),
classScope,
project.getProjectConfigVariables(),
EnumSet.of(PostProcessStep.CALCULATE_OFFSETS, PostProcessStep.RECONNECT_DEFINITIONS),
true /* follow includes */,
includeHandler);
builder.getFileNode().updateIncludeTreeLastModified(includeHandler.getLastModified());
// Make the statements inside the script tag the children of this node.
int n = fragment.getChildCount();
asNodes = new IASNode[n];
for (int i = 0; i < n; i++)
{
IASNode child = fragment.getChild(i);
asNodes[i] = child;
((NodeBase)child).setParent(this);
}
}
}
}
else
{
super.processTagSpecificAttribute(builder, tag, attribute, info);
}
}
@Override
protected void processChildNonWhitespaceUnit(MXMLTreeBuilder builder, IMXMLTagData tag,
IMXMLTextData text,
MXMLNodeInfo info)
{
info.hasDualContent = true;
}
private MXMLClassDefinitionNode getContainingClassNode()
{
MXMLClassDefinitionNode result = (MXMLClassDefinitionNode)getAncestorOfType(MXMLClassDefinitionNode.class);
assert result != null;
return result;
}
@Override
protected void initializationComplete(MXMLTreeBuilder builder, IMXMLTagData tag,
MXMLNodeInfo info)
{
super.initializationComplete(builder, tag, info);
if (info.hasSourceAttribute && info.hasDualContent)
{
ICompilerProblem problem = new MXMLDualContentProblem(tag, tag.getShortName());
builder.addProblem(problem);
return;
}
final MXMLDialect mxmlDialect = builder.getMXMLDialect();
final String sourcePath = tag.getParent().getFileSpecification().getPath();
final MXMLClassDefinitionNode classNode = getContainingClassNode();
final ASScope classScope = (ASScope)classNode.getClassDefinition().getContainedScope();
final CompilerProject project = builder.getProject();
final OffsetLookup offsetLookup = classScope.getFileScope().getOffsetLookup();
setSourcePath(sourcePath);
try
{
// parse inline ActionScript
final List<ScopedBlockNode> scriptNodes = new ArrayList<ScopedBlockNode>();
for (IMXMLUnitData unit = tag.getFirstChildUnit(); unit != null; unit = unit.getNextSiblingUnit())
{
if (unit instanceof IMXMLTextData)
{
final IMXMLTextData mxmlTextData = (IMXMLTextData)unit;
String text = mxmlTextData.getCompilableText();
if (!mxmlDialect.isWhitespace(text))
{
// local offset at the end of the containing open script tag
assert offsetLookup != null : "Expected OffsetLookup on FileScope.";
final int localOffset = mxmlTextData.getParentUnitData().getAbsoluteEnd();
final int[] absoluteOffsets = offsetLookup.getAbsoluteOffset(sourcePath, localOffset);
final int absoluteOffset = absoluteOffsets[0];
// create an include handler and mock its state as if it is
// before parsing for scope building
final IncludeHandler includeHandler = IncludeHandler.createForASTBuilding(
builder.getFileSpecificationGetter(),
sourcePath,
localOffset,
absoluteOffset);
// parse and build AST
final EnumSet<PostProcessStep> postProcess = EnumSet.of(
PostProcessStep.CALCULATE_OFFSETS,
PostProcessStep.RECONNECT_DEFINITIONS);
final ScopedBlockNode node = ASParser.parseInlineScript(
builder.getFileNode(),
mxmlTextData,
builder.getProblems(),
classScope,
project.getProjectConfigVariables(),
includeHandler,
postProcess);
MXMLFileNode filenode = builder.getFileNode();
filenode.updateIncludeTreeLastModified(includeHandler.getLastModified());
assert node != null : "Expected node from ASParser.getScopesFromInlineScript().";
scriptNodes.add(node);
}
}
}
// Make the statements inside the script tag the children of this node.
for (ScopedBlockNode script : scriptNodes)
{
int n = script.getChildCount();
asNodes = new IASNode[n];
for (int i = 0; i < n; i++)
{
IASNode child = script.getChild(i);
asNodes[i] = child;
((NodeBase)child).setParent(this);
}
}
}
catch (Exception e)
{
builder.addProblem(new UnexpectedExceptionProblem(e));
}
}
}