blob: 3d6d4692ba5734887852d587eec8f6788695aac2 [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 static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_EXCLUDE_FROM;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_ID;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_LOCAL_ID;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_INCLUDE_IN;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_ITEM_CREATION_POLICY;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ATTRIBUTE_ITEM_DESTRUCTION_POLICY;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ITEM_CREATION_POLICY_DEFERRED;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ITEM_CREATION_POLICY_IMMEDIATE;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ITEM_DESTRUCTION_POLICY_AUTO;
import static org.apache.royale.compiler.mxml.IMXMLLanguageConstants.ITEM_DESTRUCTION_POLICY_NEVER;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.IVariableDefinition;
import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.internal.mxml.MXMLDialect;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.tree.as.NodeBase;
import org.apache.royale.compiler.mxml.IMXMLTagAttributeData;
import org.apache.royale.compiler.mxml.IMXMLTagData;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.MXMLAttributeVersionProblem;
import org.apache.royale.compiler.problems.MXMLDuplicateIDProblem;
import org.apache.royale.compiler.problems.MXMLIncludeInAndExcludeFromProblem;
import org.apache.royale.compiler.problems.MXMLInvalidIDProblem;
import org.apache.royale.compiler.problems.MXMLInvalidItemCreationPolicyProblem;
import org.apache.royale.compiler.problems.MXMLInvalidItemDestructionPolicyProblem;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.mxml.IMXMLClassReferenceNode;
import org.apache.royale.compiler.tree.mxml.IMXMLInstanceNode;
public class MXMLInstanceNode extends MXMLClassReferenceNodeBase implements IMXMLInstanceNode
{
public static boolean isStateClass(String instanceType, RoyaleProject project)
{
// we are going to require that all subclasses are also named State
// but just in different packages. That way we don't have to keep resolving
// instanceType
if (!instanceType.endsWith(".State"))
return false;
if (instanceType.equals(project.getStateClass()))
return true;
IResolvedQualifiersReference instanceRef = ReferenceFactory.packageQualifiedReference(
project.getWorkspace(), instanceType);
ITypeDefinition instanceTypeDef = (ITypeDefinition)instanceRef.resolve(project);
if (project.stateClassType == null)
{
IResolvedQualifiersReference stateRef = ReferenceFactory.packageQualifiedReference(
project.getWorkspace(), project.getStateClass());
project.stateClassType = (ITypeDefinition)stateRef.resolve(project);
}
return (instanceTypeDef.isInstanceOf(project.stateClassType, project));
}
protected static MXMLInstanceNode createInstanceNode(MXMLTreeBuilder builder,
String instanceType,
NodeBase parent)
{
MXMLDialect mxmlDialect = builder.getMXMLDialect();
if (instanceType.equals(IASLanguageConstants.Boolean))
return new MXMLBooleanNode(parent);
else if (instanceType.equals(IASLanguageConstants._int))
return new MXMLIntNode(parent);
else if (instanceType.equals(IASLanguageConstants.uint))
return new MXMLUintNode(parent);
else if (instanceType.equals(IASLanguageConstants.Number))
return new MXMLNumberNode(parent);
else if (instanceType.equals(IASLanguageConstants.String))
return new MXMLStringNode(parent);
else if (instanceType.equals(IASLanguageConstants.Class))
return new MXMLClassNode(parent);
else if (instanceType.equals(IASLanguageConstants.Object))
return new MXMLObjectNode(parent);
else if (instanceType.equals(IASLanguageConstants.Array))
return new MXMLArrayNode(parent);
else if (instanceType.equals(IASLanguageConstants.Vector_qname))
return new MXMLVectorNode(parent);
else if (isStateClass(instanceType, builder.getProject()) && mxmlDialect != MXMLDialect.MXML_2006)
return new MXMLStateNode(parent);
else if (instanceType.equals(builder.getProject().getWebServiceQName()))
return new MXMLWebServiceNode(parent);
else if (instanceType.equals(builder.getProject().getRemoteObjectQName()))
return new MXMLRemoteObjectNode(parent);
else if (instanceType.equals(builder.getProject().getHTTPServiceQName()))
return new MXMLHTTPServiceNode(parent);
else if (instanceType.equals(builder.getProject().getDesignLayerQName()))
return new MXMLDesignLayerNode(parent);
else if (instanceType.equals(builder.getProject().getRepeaterQName()))
return new MXMLRepeaterNode(parent);
else if (instanceType.equals(IASLanguageConstants.XML))
return new MXMLXMLNode(parent);
else if (instanceType.equals(IASLanguageConstants.XMLList))
return new MXMLXMLListNode(parent);
else if (instanceType.equals(IASLanguageConstants.Function))
return new MXMLFunctionNode(parent);
else if (instanceType.equals(IASLanguageConstants.RegExp))
return new MXMLRegExpNode(parent);
else
return new MXMLInstanceNode(parent);
}
/**
* Constructor
*
* @param parent The parent node of this node, or <code>null</code> if there
* is no parent.
*/
MXMLInstanceNode(NodeBase parent)
{
super(parent);
}
/**
* The compile-time identifier for this instance. This is <code>null</code>
* if there is no compile-time <code>id</code> attribute on the tag that
* produced this instance.
*/
private String id;
/**
* The compile-time identifier for this instance. This is <code>null</code>
* if there is no compile-time <code>id</code> attribute on the tag that
* produced this instance. A localId does not set the id property used
* by CSS.
*/
private String localId;
/**
* The states (or state groups) that include this instance. This is
* <code>null</code> if there was no <code>includeIn</code> attribute on the
* tag that produced this instance node.
*/
private String[] includeIn;
/**
* The states (or state groups) that exclude this instance. This is
* <code>null</code> if there was no <code>excludeFrom</code> attribute on
* the tag that produced this instance node.
*/
private String[] excludeFrom;
/**
* The item creation policy for this instance, as specified by the
* <code>itemCreationPolicy</code> attribute. This is <code>null</code> if
* there was no such attribute on the tag that produced this instance node,
* or if the attribute didn't specify either <code>"immediate"</code> or
* <code>"deferred"</code>.
*/
private String itemCreationPolicy;
/**
* The item destruction policy for this instance, as specified by the
* <code>itemCreationPolicy</code> attribute. This is <code>null</code> if
* there was no such attribute on the tag that produced this instance node,
* or if the attribute didn't specify either <code>"auto"</code> or
* <code>"never"</code>.
*/
private String itemDestructionPolicy;
@Override
protected void processTagSpecificAttribute(MXMLTreeBuilder builder, IMXMLTagData tag,
IMXMLTagAttributeData attribute,
MXMLNodeInfo info)
{
if (attribute.isSpecialAttribute(ATTRIBUTE_ID))
id = processIDAttribute(builder, attribute);
else if (attribute.isSpecialAttribute(ATTRIBUTE_LOCAL_ID))
localId = processIDAttribute(builder, attribute);
else if (attribute.isSpecialAttribute(ATTRIBUTE_INCLUDE_IN))
includeIn = processIncludeInOrExcludeFromAttribute(builder, attribute);
else if (attribute.isSpecialAttribute(ATTRIBUTE_EXCLUDE_FROM))
excludeFrom = processIncludeInOrExcludeFromAttribute(builder, attribute);
else if (attribute.isSpecialAttribute(ATTRIBUTE_ITEM_CREATION_POLICY))
itemCreationPolicy = processItemCreationPolicyAttribute(builder, attribute);
else if (attribute.isSpecialAttribute(ATTRIBUTE_ITEM_DESTRUCTION_POLICY))
itemDestructionPolicy = processItemDestructionPolicyAttribute(builder, attribute);
else
super.processTagSpecificAttribute(builder, tag, attribute, info);
}
private String processIDAttribute(MXMLTreeBuilder builder,
IMXMLTagAttributeData attribute)
{
String value = attribute.getRawValue();
if (value == null)
value = "";
// Royale trims this attribute even though the old compiler didn't.
MXMLDialect mxmlDialect = builder.getMXMLDialect();
value = mxmlDialect.trim(value);
// The id must be a valid ActionScript identifier.
// Otherwise it will be null.
if (!isValidASIdentifier(value))
{
ICompilerProblem problem = new MXMLInvalidIDProblem(attribute);
builder.addProblem(problem);
return null;
}
// The id must be unique within the class.
// Otherwise it will be null.
IMXMLInstanceNode previousNodeWithSameID =
((MXMLClassDefinitionNode)getClassDefinitionNode()).addNodeWithID(value, this);
if (previousNodeWithSameID != null && !isEqual(previousNodeWithSameID))
{
ICompilerProblem problem = new MXMLDuplicateIDProblem(attribute);
builder.addProblem(problem);
return null;
}
return value;
}
protected String processItemCreationPolicyAttribute(MXMLTreeBuilder builder,
IMXMLTagAttributeData attribute)
{
MXMLDialect mxmlDialect = builder.getMXMLDialect();
if (mxmlDialect == MXMLDialect.MXML_2006)
{
ICompilerProblem problem = new MXMLAttributeVersionProblem(attribute, attribute.getName(), "2009");
builder.addProblem(problem);
return null;
}
String value = attribute.getRawValue();
value = mxmlDialect.trim(value);
// The itemCreationPolicy must be "immediate" or "deferred";
// otherwise, it will be null.
if (!value.equals(ITEM_CREATION_POLICY_IMMEDIATE) &&
!value.equals(ITEM_CREATION_POLICY_DEFERRED))
{
ICompilerProblem problem = new MXMLInvalidItemCreationPolicyProblem(attribute);
builder.addProblem(problem);
return null;
}
return value;
}
protected String processItemDestructionPolicyAttribute(MXMLTreeBuilder builder,
IMXMLTagAttributeData attribute)
{
MXMLDialect mxmlDialect = builder.getMXMLDialect();
if (mxmlDialect == MXMLDialect.MXML_2006)
{
ICompilerProblem problem = new MXMLAttributeVersionProblem(attribute, attribute.getName(), "2009");
builder.addProblem(problem);
return null;
}
String value = attribute.getRawValue();
value = mxmlDialect.trim(value);
// The itemDestructionPolicy must be "auto" or "nevet";
// otherwise, it will be null.
if (!value.equals(ITEM_DESTRUCTION_POLICY_AUTO) &&
!value.equals(ITEM_DESTRUCTION_POLICY_NEVER))
{
ICompilerProblem problem = new MXMLInvalidItemDestructionPolicyProblem(attribute);
builder.addProblem(problem);
return null;
}
return value;
}
@Override
protected void initializationComplete(MXMLTreeBuilder builder, IMXMLTagData tag,
MXMLNodeInfo info)
{
super.initializationComplete(builder, tag, info);
// If no id was specified for the instance, generate one unconditionally.
// TODO Only generate an id when necessary.
if (id == null)
((MXMLClassDefinitionNode)getClassDefinitionNode()).generateID(this);
// If both includeIn and excludeFrom are specified, set both to null.
if (includeIn != null && excludeFrom != null)
{
ICompilerProblem problem = new MXMLIncludeInAndExcludeFromProblem(tag);
builder.addProblem(problem);
includeIn = null;
excludeFrom = null;
}
RoyaleProject project = builder.getProject();
IClassDefinition classReference = getClassReference(project);
if (classReference != null)
{
String qname = classReference.getQualifiedName();
builder.addDependency(qname, DependencyType.EXPRESSION);
if (id != null)
builder.addDependency(qname, DependencyType.SIGNATURE);
}
}
@Override
public ASTNodeID getNodeID()
{
return ASTNodeID.MXMLInstanceID;
}
@Override
public String getID()
{
return id;
}
@Override
public String getLocalID()
{
return localId;
}
@Override
public String getEffectiveID()
{
return id != null ? id :
localId != null ? localId : getClassDefinitionNode().getGeneratedID(this);
}
@Override
public IVariableDefinition resolveID()
{
// Get the variable definition for the id from the class scope.
MXMLClassDefinitionNode classDefinitionNode =
(MXMLClassDefinitionNode)getClassDefinitionNode();
IClassDefinition classDefinition = classDefinitionNode.getClassDefinition();
ASScope classScope = (ASScope)classDefinition.getContainedScope();
IDefinitionSet definitionSet = classScope.getLocalDefinitionSetByName(id);
int n = definitionSet.getSize();
for (int i = 0; i < n; i++)
{
IDefinition definition = definitionSet.getDefinition(i);
if (definition instanceof IVariableDefinition)
return (IVariableDefinition)definition;
}
return null;
}
@Override
public String[] getIncludeIn()
{
return includeIn;
}
@Override
public String[] getExcludeFrom()
{
return excludeFrom;
}
@Override
public String getItemCreationPolicy()
{
return itemCreationPolicy;
}
@Override
public String getItemDestructionPolicy()
{
return itemDestructionPolicy;
}
@Override
public boolean needsDescriptor()
{
// MX containers need descriptors.
if (isContainer())
return true;
// So do any visual children (MX or Spark) of an MX container.
if (isDeferredInstantiationUIComponent())
{
IASNode parent = getParent();
if (parent instanceof IMXMLClassReferenceNode &&
((IMXMLClassReferenceNode)parent).isContainer())
{
return true;
}
}
return false;
}
@Override
public boolean needsDocumentDescriptor()
{
// MX containers need document descriptors.
// unless they are visual children of another MX container.
return isContainer() &&
getParent() instanceof IMXMLInstanceNode &&
!((IMXMLInstanceNode)getParent()).isContainer();
}
/**
* For debugging only. Builds a string such as
* <code>"spark.components.Button" id="b1"</code> from the qualified name of
* the class reference for the node and its id, if present.
*/
@Override
protected boolean buildInnerString(StringBuilder sb)
{
super.buildInnerString(sb);
String id = getID();
if (id != null)
{
sb.append(' ');
sb.append("id=");
sb.append('"');
sb.append(id);
sb.append('"');
}
return true;
}
public boolean isEqual(IMXMLInstanceNode node)
{
return (node.getLine() == getLine() &&
node.getColumn() == getColumn());
}
}