/* * * 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.mxml; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.flex.compiler.asdoc.IASDocComment; import org.apache.flex.compiler.common.ASModifier; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.common.IMetaInfo; import org.apache.flex.compiler.constants.INamespaceConstants; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IClassDefinition.ClassClassification; import org.apache.flex.compiler.definitions.metadata.IMetaTag; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.mxml.MXMLDialect; import org.apache.flex.compiler.internal.mxml.StateDefinition; import org.apache.flex.compiler.internal.mxml.StateGroupDefinition; import org.apache.flex.compiler.internal.projects.FlexProject; import org.apache.flex.compiler.internal.scopes.MXMLFileScope; import org.apache.flex.compiler.internal.scopes.TypeScope; import org.apache.flex.compiler.internal.tree.as.ImportNode; import org.apache.flex.compiler.internal.tree.as.NodeBase; import org.apache.flex.compiler.mxml.IMXMLTagAttributeData; import org.apache.flex.compiler.mxml.IMXMLTagData; import org.apache.flex.compiler.mxml.IStateDefinition; import org.apache.flex.compiler.mxml.IStateGroupDefinition; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IASScope; import org.apache.flex.compiler.tree.ASTNodeID; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.IImportNode; import org.apache.flex.compiler.tree.as.IScopedNode; import org.apache.flex.compiler.tree.metadata.IMetaTagsNode; import org.apache.flex.compiler.tree.mxml.IMXMLClassDefinitionNode; import org.apache.flex.compiler.tree.mxml.IMXMLDeclarationsNode; import org.apache.flex.compiler.tree.mxml.IMXMLEventSpecifierNode; import org.apache.flex.compiler.tree.mxml.IMXMLFileNode; import org.apache.flex.compiler.tree.mxml.IMXMLInstanceNode; import org.apache.flex.compiler.tree.mxml.IMXMLMetadataNode; import org.apache.flex.compiler.tree.mxml.IMXMLNode; import org.apache.flex.compiler.tree.mxml.IMXMLPropertySpecifierNode; import org.apache.flex.compiler.tree.mxml.IMXMLReparentNode; import org.apache.flex.compiler.tree.mxml.IMXMLScriptNode; import org.apache.flex.compiler.tree.mxml.IMXMLSpecifierNode; import org.apache.flex.compiler.tree.mxml.IMXMLStateNode; import org.apache.flex.compiler.tree.mxml.IMXMLStyleSpecifierNode; import com.google.common.collect.ArrayListMultimap; import static org.apache.flex.compiler.mxml.IMXMLLanguageConstants.*; /** * {@code MXMLClassDefinitionNode} represents an MXML tag which defines a new * class. It might be the document tag, or the tag inside a <Component> * tag, or the tag inside a <Definition> tag. */ public class MXMLClassDefinitionNode extends MXMLClassReferenceNodeBase implements IMXMLClassDefinitionNode, IScopedNode { /** * Constructor * * @param parent The parent node of this node, or <code>null</code> if there * is no parent. */ MXMLClassDefinitionNode(NodeBase parent) { super(parent); } /** * The class that this node is defining. */ private IClassDefinition classDefinition; /** * The interfaces that the class being defined claims it implements. */ @SuppressWarnings("unused") private String[] implementedInterfaces; /** * The scope inside this class. */ private IASScope scope; /** * The project for this class. */ private FlexProject project; /** * A map mapping an id to the instance node with that id. */ private Map<String, IMXMLInstanceNode> idMap; /** * A map mapping the name of a state defined within this class to the state * node with that name. */ private Map<String, StateDefinition> stateMap; /** * A map mapping the name of a state group defined within this class to a * set of state names for the states contained by the group. Example: "odd" * -> { "s1", "s3", "s5" } */ private Map<String, StateGroupDefinition> groupMap; /** * A list of all state-dependent nodes in this class, in tree order. This * includes instance nodes with includeIn/excludeFrom, and * property/style/event nodes with suffixes. This is a temporary list that * is used to build stateDependentNodeMap and then freed. */ private List<IMXMLNode> stateDependentNodeList; /** * A map mapping the the name of a state defined within this class to a list * of nodes (in tree order) that depend on that class. These node lists for * each state are used by the code generator to generate the IOverride * objects for each state. */ private ArrayListMultimap<String, IMXMLNode> stateDependentNodeMap; /** * The state name to which <code>currentState</code> should be initialized. */ private String initialState; /** * An incrementing counter for compiler-generated ids within this class. */ private int generatedIDCounter = 0; /** * A map mapping an instance node without an id to its autogenerated id. */ Map<IMXMLInstanceNode, String> generatedIDMap = new HashMap<IMXMLInstanceNode, String>(); /** * The child <Metadata> nodes. */ private IMXMLMetadataNode[] metadataNodes; /** * The child <Script> nodes. */ private IMXMLScriptNode[] scriptNodes; /** * The child <Declarations> nodes. */ private IMXMLDeclarationsNode[] declarationsNodes; /** * This definition link is used only by CodeModel. It is created and updated * by {@code getAdapter()}. */ /** * This counter keeps track of how many {@code MXMLComponentNode}s are * inside this {@code MXMLClassDefinitionNode}. The counter is incorporated * into the autogenerated name of the <code><fx:Component></code> * class. */ private int componentCount = 0; /** * This flag keep track if there any data binding nodes in this class. */ private boolean hasDataBindings; private IASDocComment asDocComment; @Override protected void initializationComplete(MXMLTreeBuilder builder, IMXMLTagData tag, MXMLNodeInfo info) { super.initializationComplete(builder, tag, info); // Revisit all State nodes in this class after all the states are known, // to determine the state groups and which states are in which groups. reprocessStateNodes(); // Revisit all state-dependent nodes in this class after the states // and state groups are known, to determine which of them depend on which state // (since the code generator needs this information to create IOverride // objects for each state). reprocessStateDependentNodes(builder); // Add the dependency between the class this node defines and its superclass, // as expressed by this tag that created this node. project = builder.getProject(); IClassDefinition classReference = getClassReference(project); String qname = classReference.getQualifiedName(); builder.addDependency(qname, DependencyType.INHERITANCE); } @Override protected void processTagSpecificAttribute(MXMLTreeBuilder builder, IMXMLTagData tag, IMXMLTagAttributeData attribute, MXMLNodeInfo info) { if (attribute.isSpecialAttribute(ATTRIBUTE_IMPLEMENTS)) { // Keep track of the specified interfaces as an array of Strings, // for use by the compiler. String rawValue = attribute.getRawValue(); if (rawValue != null) implementedInterfaces = attribute.getMXMLDialect().splitAndTrim(rawValue); // For CodeModel's use, also keep track of them as children // of an {@code MXMLImplementsNode}. MXMLImplementsNode interfaceNode = new MXMLImplementsNode(this); interfaceNode.initializeFromAttribute(builder, attribute); info.addChildNode(interfaceNode); // TODO Report problems if the interfaces don't exist // Later we also have report a problems for each interface method // that isn't implemented. } else { super.processTagSpecificAttribute(builder, tag, attribute, info); } } @Override protected void processChildTag(MXMLTreeBuilder builder, IMXMLTagData tag, IMXMLTagData childTag, MXMLNodeInfo info) { MXMLFileScope fileScope = builder.getFileScope(); project = builder.getProject(); MXMLNodeBase childNode = null; if (fileScope.isDeclarationsTag(childTag)) { childNode = new MXMLDeclarationsNode(this); } else if ((fileScope.isScriptTag(childTag)) && // Our base class will handle script tags for MXML 2009 and below. // Beginning with MXML 2012, only class definition tags may have script // tags as children. (builder.getMXMLDialect().isEqualToOrAfter(MXMLDialect.MXML_2012))) { childNode = new MXMLScriptNode(this); } else if (fileScope.isStyleTag(childTag)) { childNode = new MXMLStyleNode(this); } else if (fileScope.isMetadataTag(childTag)) { childNode = new MXMLMetadataNode(this); } else if (fileScope.isBindingTag(childTag)) { childNode = new MXMLBindingNode(this); this.setHasDataBindings(); // we must have some, if we made this node } else { super.processChildTag(builder, tag, childTag, info); } if (childNode != null) { childNode.initializeFromTag(builder, childTag); info.addChildNode(childNode); } } @Override public ASTNodeID getNodeID() { return ASTNodeID.MXMLClassDefinitionID; } @Override public String getPackageName() { // getPackageName() in NodeBase expects to find an IPackageNode. // MXML trees don't have package nodes, so instead we just ask // the corresponding IClassDefinition for its package name. return classDefinition.getPackageName(); } @Override public IClassDefinition getClassDefinition() { return classDefinition; } /** * Sets the definition of the class defined by this node. * * @param classDefinition An {@code IClassDefinition} object for the class. */ void setClassDefinition(IClassDefinition classDefinition) { this.classDefinition = classDefinition; setScope((TypeScope)classDefinition.getContainedScope()); } /** * Adds an entry to this class's id map, which maps an id to the node with * that id. * <p> * If a previously processed tag had the same id, the specified node is not * added to the map, and the old node is returned; otherwise * <code>null</code> is returned. * * @param node An {@code IMXMLInstanceNode} with an id. * @return The {@code IMXMLInstanceNode}, if any, that already has the same * id. */ IMXMLInstanceNode addNodeWithID(String id, IMXMLInstanceNode node) { if (idMap == null) idMap = new HashMap<String, IMXMLInstanceNode>(); return idMap.put(id, node); } @Override public IMXMLInstanceNode getNodeWithID(String id) { return idMap != null ? idMap.get(id) : null; } /* * Notes about MXML States Each component within a document has its own set * of states and state groups, so these are managed by class definition * nodes. Only classes that implement mx.core.IStateClient2 can have states. * State tags can occur wherever a value of type mx.states.State would be * allowed. Usually <State> tags are written as the value of the <states> * property of the UIComponent-derived component that they're defining. But * they could also go into <Declarations>, or be used as other property * values. In MXML 2006, <mx:State> is a normal instance tag whose runtime * behavior is to apply a particular state. You specify an array of * IOverride objects to be executed when the state is entered by setting its * 'overrides' property (which is the default property). Various * implementations of IOverride handle setting properties and styles, and * adding event handlers, on specified objects, or creating instances of * objects. For example, you might write <mx:State name="s1"> * <mx:SetProperty target="b1" name="label" value="OK"/> <mx:SetStyle * target="b1" name="color" value="0xFFFFFF"/> </mx:State> to change the * label and color of Button b1. MXML 2009 introduced state-suffix notation * (such as label.s1="OK or <mx:label.s1>OK<mx:label.s1> for specifying * state-dependent properties/styles/events, and includeIn/excludeFrom * attributes for specifying instances that exist only in particular states. * These two MXML features end up autogenerating the override objects for * the states, and you can no longer specify the overrides explicitly in * MXML. Consider the following example: <s:Application ...> <s:Button * id="b1" label="OK"/> <s:Button id="b2" label.even="Foo" label.odd="Bar"/> * <s:Button id="b3" includeIn="odd,s4"/> <s:Button id="b4" * excludeFrom="even,s3"/> <s:states> <s:State name="s1" groups="all,odd"/> * <s:State name="s2" groups="all,even"/> <s:State name="s3" * groups="all,odd"/> <s:State name="s4" groups="all,even"/> <s:State * name="s5" groups="all,odd"/> </s:state> </s:Application> The class * defined by the <s:Application> tag has five states (named "s1", "s2", * "s3", "s4", and "s5") and three state groups (named "all", "odd", and * "even".) Note that state groups are defined implicitly by being mentioned * on <State> tags. Also, the names of states and state groups must be * distinct. The members of the three groups are: all: s1, s2, s3, s4, s5 * odd: s1, s3, s5 even: s4, s4 Button b1 and its label property are not * state-dependent. Button b2 is not a state-dependent instance, but it has * a state-dependent label property. Buttons b3 and b4 are state-dependent * instances; b3 exists in states "s1", "s3", "s4", "s5" but not in "s2"; b4 * exists in states "s1" and "s5" but not in "s2", "s3", or "s4". As tree * nodes are created, their MXMLClassDefinitionNode keeps track of <State> * nodes and any state-dependent node (i.e., one with includeIn/excludeFrom * or a suffix). After all <State> nodes have been discovered, we reprocess * them to determine all the state groups. After we know the names of all * the states and state groups, we reprocess the state-dependent nodes to * check that they specify valid states or state groups and determine what * states they belong to. We build lists of state-dependent nodes for each * state, like this: s1 MXMLPropertyNode for attribute label.odd="Bar" on * Button b2 MXMLInstanceNode for Button b3 MXMLInstanceNode for Button b4 * s2 MXMLPropertyNode for attribute label.even="Foo" on Button b2 s3 * MXMLPropertyNode for attribute label.odd="Bar" on Button b2 * MXMLInstanceNode for Button b3 s4 MXMLPropertyNode for attribute * label.even="Foo" on Button b2 MXMLInstanceNode for Button b3 s5 * MXMLPropertyNode for attribute label.odd="Bar" on Button b2 * MXMLInstanceNode for Button b3 MXMLInstanceNode for Button b4 The code * generator then uses these lists to generate the override objects that * each state object applies. */ @Override public Set<String> getStateNames() { if (stateMap == null) return new HashSet<String>(0); return stateMap.keySet(); } @Override public Set<IStateDefinition> getStates() { if (stateMap == null) return new HashSet<IStateDefinition>(0); Set<IStateDefinition> states = new HashSet<IStateDefinition>(stateMap.size()); states.addAll(stateMap.values()); return states; } /** * Gets the state node that defines a specified state. * * @param stateName A state name. * @return The {code IMXMLStateNode} with that name. */ public IMXMLStateNode getStateNode(String stateName) { if (stateMap == null) return null; return stateMap.get(stateName).getNode(); } /** * Gets the state definition that defines a specified state. * * @param stateName A state name * @return The {code IStateDefinition} with that name */ public IStateDefinition getStateByName(String stateName) { if (stateMap == null) return null; return stateMap.get(stateName); } /** * Gets the state group that defines a specified state group. * * @param groupName A state group name * @return The {code IStateGroup} with that name */ public IStateGroupDefinition getStateGroupByName(String groupName) { if (groupMap == null) return null; return groupMap.get(groupName); } /** * Determines whether a string is the name of a state of this class. * * @param stateName A String that might be a state name. * @return <code>true</code> if it is a state name. */ public boolean isState(String stateName) { if (stateMap == null) return false; return stateMap.containsKey(stateName); } @Override public Set<String> getStateGroupNames() { if (groupMap == null) return new HashSet<String>(0); return groupMap.keySet(); } @Override public Set<IStateGroupDefinition> getStateGroups() { if (groupMap == null) return new HashSet<IStateGroupDefinition>(0); Set<IStateGroupDefinition> groups = new HashSet<IStateGroupDefinition>(groupMap.size()); groups.addAll(groupMap.values()); return groups; } private String[] getStatesInGroup(String groupName) { return groupMap.get(groupName).getIncludedStates(); } /** * Determines whether a string is the name of a state group of this class. * * @param s A String that might be a state group name. * @return <code>true</code> if it is a state group name. */ private boolean isStateGroup(String groupName) { if (groupMap == null) return false; return groupMap.containsKey(groupName); } @Override public List<IMXMLNode> getNodesDependentOnState(String stateName) { return stateDependentNodeMap != null ? stateDependentNodeMap.get(stateName) : null; } @Override public List<IMXMLNode> getAllStateDependentNodes() { return stateDependentNodeList; } @Override public String getInitialState() { return initialState; } /** * Iterates over the State nodes to determine the state groups defined * within this class. This initializes the stateGroupMap that's used by * getStateGroups(), getStatesInGroup(), and isStateGroup(). */ private void reprocessStateNodes() { Set<String> states = getStateNames(); if (states == null) return; for (String state : states) { IMXMLStateNode stateNode = getStateNode(state); String[] stateGroups = stateNode.getStateGroups(); if (stateGroups != null) { for (String stateGroup : stateGroups) { addStateToStateGroup(stateGroup, state); } } } } private void addStateToStateGroup(String groupName, String stateName) { if (groupMap == null) groupMap = new HashMap<String, StateGroupDefinition>(); StateDefinition state = stateMap.get(stateName); StateGroupDefinition group = groupMap.get(groupName); if (group == null) { group = new StateGroupDefinition(groupName, getDefinition()); groupMap.put(groupName, group); } state.addGroup(group); group.addState(state); } /** * Determines which nodes depend on which states. Instance nodes depend on * the states implied by their includeIn and excludeFrom attributes. * Property, style, and event nodes depend on the states implied by their * suffix (as in label.up="OK"). */ private void reprocessStateDependentNodes(MXMLTreeBuilder builder) { if (stateDependentNodeList == null) return; // Flags that keep track of whether we have various kinds of stateful nodes. // Each kind introduce particular dependencies on various runtime classes. boolean haveInstanceOverride = false; boolean havePropertyOverride = false; boolean haveStyleOverride = false; boolean haveEventOverride = false; // Iterate over all instance nodes that have includeIn or excludeFrom, // and all property/style/event nodes that have a suffix. for (IMXMLNode node : stateDependentNodeList) { if (node instanceof IMXMLInstanceNode || node instanceof IMXMLReparentNode) { haveInstanceOverride = true; // TODO Consider introducing an interface // containing getIncludeIn() and getExcludeFrom() // to make IMXMLInstanceNode and IMXMLReparentNode // look alike here. String[] includeIn = null; if (node instanceof IMXMLInstanceNode) includeIn = ((IMXMLInstanceNode)node).getIncludeIn(); else if (node instanceof IMXMLReparentNode) includeIn = ((IMXMLReparentNode)node).getIncludeIn(); String[] excludeFrom = null; if (node instanceof IMXMLInstanceNode) excludeFrom = ((IMXMLInstanceNode)node).getExcludeFrom(); else if (node instanceof IMXMLReparentNode) excludeFrom = ((IMXMLReparentNode)node).getExcludeFrom(); // Determine which states contain this instance // based on includeIn or excludeFrom. Set<String> statesContainingInstance = null; if (includeIn != null) statesContainingInstance = processIncludeIn(includeIn); else if (excludeFrom != null) statesContainingInstance = processExcludeFrom(excludeFrom); // Add the node to each of those state's list of state-dependent nodes. if (statesContainingInstance != null) { for (String state : statesContainingInstance) { addStateDependentNode(state, node); } } } else if (node instanceof IMXMLSpecifierNode) { // havePropertyOverride must be last because // IMXMLStyleSpecifierNode extends IMXMLPropertySpecifierNode if (node instanceof IMXMLStyleSpecifierNode) haveStyleOverride = true; else if (node instanceof IMXMLEventSpecifierNode) haveEventOverride = true; else if (node instanceof IMXMLPropertySpecifierNode) havePropertyOverride = true; String suffix = ((IMXMLSpecifierNode)node).getSuffix(); if (isState(suffix)) { // For a specifier node like label.s1="OK", // add the node to the s1's list of state-dependent nodes. addStateDependentNode(suffix, node); } else if (isStateGroup(suffix)) { // For a specifier node like label.g1="OK", // add the node to the state-dependent node list // of each state in group g1. for (String state : getStatesInGroup(suffix)) { addStateDependentNode(state, node); } } } } FlexProject project = builder.getProject(); if (haveInstanceOverride) builder.addExpressionDependency(project.getInstanceOverrideClass()); if (havePropertyOverride) builder.addExpressionDependency(project.getPropertyOverrideClass()); if (haveStyleOverride) builder.addExpressionDependency(project.getStyleOverrideClass()); if (haveEventOverride) builder.addExpressionDependency(project.getEventOverrideClass()); } private Set<String> processIncludeIn(String[] includeIn) { // Start with an empty set. Set<String> applicableStates = new HashSet<String>(); // Add the included states or groups of states. for (String item : includeIn) { if (isState(item)) { applicableStates.add(item); } else if (isStateGroup(item)) { for (String state : getStatesInGroup(item)) { applicableStates.add(state); } } } return applicableStates; } private Set<String> processExcludeFrom(String[] excludeFrom) { // Start with the set of all states. Set<String> applicableStates = new HashSet<String>(); for (String state : getStateNames()) { applicableStates.add(state); } // Remove the excluded states or groups of states. for (String item : excludeFrom) { if (isState(item)) { applicableStates.remove(item); } else if (isStateGroup(item)) { for (String state : getStatesInGroup(item)) { applicableStates.remove(state); } } } return applicableStates; } public void generateID(IMXMLInstanceNode instanceNode) { if (instanceNode != null && instanceNode.getID() == null) { if (generatedIDMap.containsKey(instanceNode)) return; String id = project.getGeneratedIDBase() + generatedIDCounter++; generatedIDMap.put(instanceNode, id); } } void addStateDependentNode(MXMLTreeBuilder builder, IMXMLNode node) { if (stateDependentNodeList == null) stateDependentNodeList = new ArrayList<IMXMLNode>(); stateDependentNodeList.add(node); // The codegen for a state-dependent instance node will require // an autogenerated id if that node doesn't have a specified id. // The codegen for a state-dependent property/style/event node // will require an autogenerated id for the target instance node // if the instance node doesn't have a specified id. IMXMLInstanceNode instanceNode = null; if (node instanceof IMXMLInstanceNode) instanceNode = (IMXMLInstanceNode)node; // in case of root node, parent won't be IMXMLInstanceNode else if (node instanceof IMXMLSpecifierNode && node.getParent() instanceof IMXMLInstanceNode) instanceNode = (IMXMLInstanceNode)node.getParent(); generateID(instanceNode); } @Override public String getGeneratedID(IMXMLInstanceNode instanceNode) { return generatedIDMap.get(instanceNode); } private void addStateDependentNode(String state, IMXMLNode node) { if (stateDependentNodeMap == null) stateDependentNodeMap = ArrayListMultimap.create(); stateDependentNodeMap.put(state, node); } /** * Adds an entry to this class's state map, which maps state names to state * nodes. * <p> * If a previously processed state node had the same name, the specified * node is not added to the map, and the old node is returned; otherwise * <code>null</code> is returned. * * @param node An {@code IMXMLStateNode} with an name. * @return The {@code IMXMLStateNode}, if any, that already has the same * name. */ IMXMLStateNode addStateNode(IMXMLStateNode node) { if (stateMap == null) { stateMap = new HashMap<String, StateDefinition>(); // The first state we find is the initial state. // TODO Should it be the first one in the 'states' property? initialState = node.getStateName(); } StateDefinition oldState = stateMap.put(node.getStateName(), (StateDefinition)node.getDefinition()); return oldState != null ? oldState.getNode() : null; } @Override public IMXMLMetadataNode[] getMetadataNodes() { return metadataNodes; } @Override public IMXMLScriptNode[] getScriptNodes() { return scriptNodes; } @Override public IMXMLDeclarationsNode[] getDeclarationsNodes() { return declarationsNodes; } @Override void setChildren(IMXMLNode[] children) { super.setChildren(children); if (children != null) { List<IMXMLMetadataNode> metadataNodes = new ArrayList<IMXMLMetadataNode>(); List<IMXMLScriptNode> scriptNodes = new ArrayList<IMXMLScriptNode>(); List<IMXMLDeclarationsNode> declarationsNodes = new ArrayList<IMXMLDeclarationsNode>(); for (IMXMLNode child : children) { if (child instanceof IMXMLMetadataNode) metadataNodes.add((IMXMLMetadataNode)child); else if (child instanceof IMXMLScriptNode) scriptNodes.add((IMXMLScriptNode)child); else if (child instanceof IMXMLDeclarationsNode) declarationsNodes.add((IMXMLDeclarationsNode)child); } this.metadataNodes = metadataNodes.toArray(new IMXMLMetadataNode[0]); this.scriptNodes = scriptNodes.toArray(new IMXMLScriptNode[0]); this.declarationsNodes = declarationsNodes.toArray(new IMXMLDeclarationsNode[0]); } } @Override public IScopedNode getScopedNode() { return this; } @Override public IASScope getScope() { return scope; } /** * Sets the class scope. * * @param scope A {@code TypeScope} object for the scope contained by the * class defined by this tag. */ public void setScope(TypeScope scope) { this.scope = scope; } @Override public void getAllImports(Collection<String> imports) { ArrayList<IImportNode> importNodes = new ArrayList<IImportNode>(); getAllImportNodes(importNodes); for (IImportNode importNode : importNodes) imports.add(importNode.getImportName()); } @Override public void getAllImportNodes(Collection<IImportNode> imports) { // Add implicit import nodes created by MXML tags. // The implicit imports for each MXML class are stored on its ClassDefinition. IMXMLFileNode fileNode = (IMXMLFileNode)getAncestorOfType(IMXMLFileNode.class); ICompilerProject project = fileNode.getCompilerProject(); for (String qname : ((ClassDefinition)classDefinition).getImplicitImports()) { imports.add(new MXMLImplicitImportNode(project, qname)); } // Add the implicit import nodes associated with the file node. fileNode.getAllImportNodes(imports); // Add the explicit import nodes inside of <Script> tags. for (IMXMLScriptNode scriptNode : getScriptNodes()) { for (IASNode node : scriptNode.getASNodes()) { if (node instanceof ImportNode) imports.add((IImportNode)node); else ((NodeBase)node).collectImportNodes(imports); } } } @Override public IExpressionNode getNameExpressionNode() { // The name is determined by the file, not by anything in the file. return null; } @Override public int getNameStart() { // The name is determined by the file, not by anything in the file. return -1; } @Override public int getNameEnd() { // The name is determined by the file, not by anything in the file. return -1; } @Override public int getNameAbsoluteStart() { return getNameStart(); } @Override public int getNameAbsoluteEnd() { return getNameEnd(); } @Override public String getQualifiedName() { return classDefinition.getQualifiedName(); } @Override public String getShortName() { return classDefinition.getBaseName(); } @Override public boolean hasModifier(ASModifier modifier) { return classDefinition.hasModifier(modifier); } @Override public boolean hasNamespace(String namespace) { return getNamespace().equals(namespace); } @Override public String getNamespace() { return INamespaceConstants.public_; } @Override public boolean isImplicit() { return false; } @Override public IMetaTagsNode getMetaTags() { // TODO Auto-generated method stub return null; } @Override public IMetaInfo[] getMetaInfos() { return classDefinition.getAllMetaTags(); } @Override public IClassDefinition getDefinition() { return classDefinition; } /** * Used only for debugging. */ @SuppressWarnings("unused") private void dumpStateDependentNodes() { Set<String> states = getStateNames(); if (states == null) return; for (String state : states) { System.out.println("State " + state); List<IMXMLNode> nodes = getNodesDependentOnState(state); if (nodes != null) { for (IMXMLNode node : nodes) { System.out.println(" " + node); } } } } @Override public String getBaseClassName() { return getDefinition().getBaseClassAsDisplayString(); } @Override public String[] getImplementedInterfaces() { return getDefinition().getImplementedInterfacesAsDisplayStrings(); } @Override public IMetaTag[] getMetaTagsByName(String name) { return getDefinition().getMetaTagsByName(name); } @Override public ClassClassification getClassClassification() { return ClassClassification.PACKAGE_MEMBER; } @Override public IASDocComment getASDocComment() { return asDocComment; } @Override public boolean hasExplicitComment() { return asDocComment != null; } /** * Notifies this class definition node that an "inner" <fx:Component> has * been found. In response, this methods returns an autogenerated name for * the component class. */ String addComponent() { // Return a string such as "MyAppInnerClass0" or "MyCompInnerClass2InnerClass1". StringBuilder sb = new StringBuilder(); sb.append(getShortName()); sb.append("InnerClass"); sb.append(componentCount); componentCount++; return sb.toString(); } @Override public boolean needsDescriptor() { return isContainer(); } @Override public boolean needsDocumentDescriptor() { return isContainer(); } public void setHasDataBindings() { hasDataBindings = true; } @Override public boolean getHasDataBindings() { return hasDataBindings; } public void setASDocComment(IASDocComment ref) { asDocComment = ref; } }