http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/c3dce49f/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/mxml/flexjs/MXMLFlexJSEmitter.java ---------------------------------------------------------------------- diff --git a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/mxml/flexjs/MXMLFlexJSEmitter.java b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/mxml/flexjs/MXMLFlexJSEmitter.java new file mode 100644 index 0000000..094f5c7 --- /dev/null +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/mxml/flexjs/MXMLFlexJSEmitter.java @@ -0,0 +1,2320 @@ +/* + * + * 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.codegen.mxml.flexjs; + + +import java.io.File; +import java.io.FilterWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.flex.abc.semantics.MethodInfo; +import org.apache.flex.abc.semantics.Name; +import org.apache.flex.abc.semantics.Namespace; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.codegen.mxml.flexjs.IMXMLFlexJSEmitter; +import org.apache.flex.compiler.constants.IASKeywordConstants; +import org.apache.flex.compiler.constants.IASLanguageConstants; +import org.apache.flex.compiler.definitions.IClassDefinition; +import org.apache.flex.compiler.definitions.IDefinition; +import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens; +import org.apache.flex.compiler.internal.codegen.databinding.BindingDatabase; +import org.apache.flex.compiler.internal.codegen.databinding.BindingInfo; +import org.apache.flex.compiler.internal.codegen.databinding.FunctionWatcherInfo; +import org.apache.flex.compiler.internal.codegen.databinding.PropertyWatcherInfo; +import org.apache.flex.compiler.internal.codegen.databinding.StaticPropertyWatcherInfo; +import org.apache.flex.compiler.internal.codegen.databinding.WatcherInfoBase; +import org.apache.flex.compiler.internal.codegen.databinding.WatcherInfoBase.WatcherType; +import org.apache.flex.compiler.internal.codegen.databinding.XMLWatcherInfo; +import org.apache.flex.compiler.internal.codegen.js.JSSessionModel.PropertyNodes; +import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitter; +import org.apache.flex.compiler.internal.codegen.js.flexjs.JSFlexJSEmitterTokens; +import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; +import org.apache.flex.compiler.internal.codegen.js.jx.PackageFooterEmitter; +import org.apache.flex.compiler.internal.codegen.js.utils.EmitterUtils; +import org.apache.flex.compiler.internal.codegen.mxml.MXMLEmitter; +import org.apache.flex.compiler.internal.projects.FlexJSProject; +import org.apache.flex.compiler.internal.projects.FlexProject; +import org.apache.flex.compiler.internal.scopes.ASProjectScope; +import org.apache.flex.compiler.internal.tree.as.FunctionCallNode; +import org.apache.flex.compiler.internal.tree.as.IdentifierNode; +import org.apache.flex.compiler.internal.tree.as.MemberAccessExpressionNode; +import org.apache.flex.compiler.projects.ICompilerProject; +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.IFunctionNode; +import org.apache.flex.compiler.tree.as.IIdentifierNode; +import org.apache.flex.compiler.tree.as.IImportNode; +import org.apache.flex.compiler.tree.as.IVariableNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagsNode; +import org.apache.flex.compiler.tree.mxml.IMXMLArrayNode; +import org.apache.flex.compiler.tree.mxml.IMXMLClassDefinitionNode; +import org.apache.flex.compiler.tree.mxml.IMXMLClassNode; +import org.apache.flex.compiler.tree.mxml.IMXMLComponentNode; +import org.apache.flex.compiler.tree.mxml.IMXMLDataBindingNode; +import org.apache.flex.compiler.tree.mxml.IMXMLDeclarationsNode; +import org.apache.flex.compiler.tree.mxml.IMXMLDocumentNode; +import org.apache.flex.compiler.tree.mxml.IMXMLEventSpecifierNode; +import org.apache.flex.compiler.tree.mxml.IMXMLFactoryNode; +import org.apache.flex.compiler.tree.mxml.IMXMLImplementsNode; +import org.apache.flex.compiler.tree.mxml.IMXMLInstanceNode; +import org.apache.flex.compiler.tree.mxml.IMXMLLiteralNode; +import org.apache.flex.compiler.tree.mxml.IMXMLMetadataNode; +import org.apache.flex.compiler.tree.mxml.IMXMLNode; +import org.apache.flex.compiler.tree.mxml.IMXMLObjectNode; +import org.apache.flex.compiler.tree.mxml.IMXMLPropertySpecifierNode; +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.IMXMLStringNode; +import org.apache.flex.compiler.tree.mxml.IMXMLStyleSpecifierNode; +import org.apache.flex.compiler.units.ICompilationUnit; +import org.apache.flex.compiler.utils.NativeUtils; +import org.apache.flex.compiler.visitor.mxml.IMXMLBlockWalker; + +import com.google.common.base.Joiner; + +/** + * @author Erik de Bruin + */ +public class MXMLFlexJSEmitter extends MXMLEmitter implements + IMXMLFlexJSEmitter +{ + + // the instances in a container + private ArrayList<MXMLDescriptorSpecifier> currentInstances; + private ArrayList<MXMLDescriptorSpecifier> currentPropertySpecifiers; + private ArrayList<MXMLDescriptorSpecifier> descriptorTree; + private MXMLDescriptorSpecifier propertiesTree; + private MXMLDescriptorSpecifier currentStateOverrides; + private ArrayList<MXMLEventSpecifier> events; + // all instances in the current document or subdocument + private ArrayList<MXMLDescriptorSpecifier> instances; + // all instances in the document AND its subdocuments + private ArrayList<MXMLDescriptorSpecifier> allInstances = new ArrayList<MXMLDescriptorSpecifier>(); + private ArrayList<MXMLScriptSpecifier> scripts; + //private ArrayList<MXMLStyleSpecifier> styles; + private IClassDefinition classDefinition; + private IClassDefinition documentDefinition; + private ArrayList<String> usedNames = new ArrayList<String>(); + private ArrayList<IMXMLMetadataNode> metadataNodes = new ArrayList<IMXMLMetadataNode>(); + + private int eventCounter; + private int idCounter; + private int bindingCounter; + + private boolean inMXMLContent; + private boolean inStatesOverride; + private boolean makingSimpleArray; + + private StringBuilder subDocuments = new StringBuilder(); + private ArrayList<String> subDocumentNames = new ArrayList<String>(); + private String interfaceList; + + /** + * This keeps track of the entries in our temporary array of + * DeferredInstanceFromFunction objects that we CG to help with + * State override CG. + * + * Keys are Instance nodes, + * values are the array index where the deferred instance is: + * + * deferred instance = local3[ nodeToIndexMap.get(an instance) ] + */ + protected Map<IMXMLNode, Integer> nodeToIndexMap; + + public MXMLFlexJSEmitter(FilterWriter out) + { + super(out); + } + + @Override + public String postProcess(String output) + { + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()).getASEmitter(); + usedNames.addAll(((JSFlexJSEmitter)asEmitter).usedNames); + + boolean foundXML = false; + String[] lines = output.split("\n"); + ArrayList<String> finalLines = new ArrayList<String>(); + int endRequires = -1; + boolean sawRequires = false; + boolean stillSearching = true; + for (String line : lines) + { + if (stillSearching) + { + int c = line.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); + if (c > -1) + { + int c2 = line.indexOf(")"); + String s = line.substring(c + 14, c2 - 1); + if (s.equals(IASLanguageConstants.XML)) + { + foundXML = true; + } + sawRequires = true; + if (!usedNames.contains(s)) + continue; + } + else if (sawRequires) + { + stillSearching = false; + endRequires = finalLines.size(); + } + } + finalLines.add(line); + } + boolean needXML = ((FlexJSProject)(((IMXMLBlockWalker) getMXMLWalker()).getProject())).needXML; + if (needXML && !foundXML) + { + StringBuilder appendString = new StringBuilder(); + appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); + appendString.append(ASEmitterTokens.PAREN_OPEN.getToken()); + appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); + appendString.append(IASLanguageConstants.XML); + appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); + appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken()); + appendString.append(ASEmitterTokens.SEMICOLON.getToken()); + finalLines.add(endRequires, appendString.toString()); + // TODO (aharui) addLineToMappings(finalLines.size()); + } + // append info() structure if main CU + ICompilerProject project = getMXMLWalker().getProject(); + if (project instanceof FlexJSProject) + { + FlexJSProject flexJSProject = (FlexJSProject) project; + String mainDef = null; + try { + mainDef = flexJSProject.mainCU.getQualifiedNames().get(0); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + String thisDef = documentDefinition.getQualifiedName(); + if (mainDef != null && mainDef.equals(thisDef)) + { + Set<String> mixins = flexJSProject.config.getIncludes(); + if (mixins.size() > 0) + { + String infoInject = "\n\n" + thisDef + ".prototype.info = function() {\n" + + " return { mixins: ["; + boolean firstOne = true; + for (String mixin : mixins) + { + if (!firstOne) + infoInject += ", "; + infoInject += mixin; + firstOne = false; + StringBuilder appendString = new StringBuilder(); + appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()); + appendString.append(ASEmitterTokens.PAREN_OPEN.getToken()); + appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); + appendString.append(mixin); + appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken()); + appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken()); + appendString.append(ASEmitterTokens.SEMICOLON.getToken()); + finalLines.add(endRequires, appendString.toString()); + //addLineToMappings(finalLines.size()); + } + infoInject += "]}};"; + finalLines.add(infoInject); + //addLineToMappings(finalLines.size()); + } + } + } + return Joiner.on("\n").join(finalLines); + } + + @Override + protected String getIndent(int numIndent) + { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < numIndent; i++) + sb.append(JSFlexJSEmitterTokens.INDENT.getToken()); + return sb.toString(); + } + + //-------------------------------------------------------------------------- + + @Override + public void emitDeclarations(IMXMLDeclarationsNode node) + { + inMXMLContent = true; + MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i"); + + MXMLDescriptorSpecifier currentPropertySpecifier = new MXMLDescriptorSpecifier(); + currentPropertySpecifier.isProperty = true; + currentPropertySpecifier.name = "mxmlContent"; + currentPropertySpecifier.parent = currentInstance; + descriptorTree.add(currentPropertySpecifier); + moveDown(false, currentInstance, currentPropertySpecifier); + super.emitDeclarations(node); + moveUp(false, false); + inMXMLContent = false; + } + + @Override + public void emitDocument(IMXMLDocumentNode node) + { + descriptorTree = new ArrayList<MXMLDescriptorSpecifier>(); + propertiesTree = new MXMLDescriptorSpecifier(); + + events = new ArrayList<MXMLEventSpecifier>(); + instances = new ArrayList<MXMLDescriptorSpecifier>(); + scripts = new ArrayList<MXMLScriptSpecifier>(); + //styles = new ArrayList<MXMLStyleSpecifier>(); + + currentInstances = new ArrayList<MXMLDescriptorSpecifier>(); + currentStateOverrides = new MXMLDescriptorSpecifier(); + currentPropertySpecifiers = new ArrayList<MXMLDescriptorSpecifier>(); + + eventCounter = 0; + idCounter = 0; + bindingCounter = 0; + + // visit MXML + IClassDefinition cdef = node.getClassDefinition(); + classDefinition = cdef; + documentDefinition = cdef; + + // TODO (mschmalle) will remove this cast as more things get abstracted + JSFlexJSEmitter fjs = (JSFlexJSEmitter) ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + + fjs.getModel().setCurrentClass(cdef); + + // visit tags + final int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + getMXMLWalker().walk(node.getChild(i)); + } + + String cname = node.getFileNode().getName(); + + emitHeader(node); + + emitClassDeclStart(cname, node.getBaseClassName(), false); + + emitComplexInitializers(node); + + emitPropertyDecls(); + + emitClassDeclEnd(cname, node.getBaseClassName()); + + emitMetaData(cdef); + + write(subDocuments.toString()); + writeNewline(); + + emitScripts(); + + fjs.getBindableEmitter().emit(cdef); + fjs.getAccessorEmitter().emit(cdef); + + emitEvents(cname); + + emitPropertyGetterSetters(cname); + + emitMXMLDescriptorFuncs(cname); + + emitBindingData(cname, cdef); + } + + public void emitSubDocument(IMXMLComponentNode node) + { + ArrayList<MXMLDescriptorSpecifier> oldDescriptorTree; + MXMLDescriptorSpecifier oldPropertiesTree; + ArrayList<MXMLEventSpecifier> oldEvents; + ArrayList<MXMLScriptSpecifier> oldScripts; + ArrayList<MXMLDescriptorSpecifier> oldCurrentInstances; + ArrayList<MXMLDescriptorSpecifier> oldInstances; + ArrayList<MXMLDescriptorSpecifier> oldCurrentPropertySpecifiers; + int oldEventCounter; + int oldIdCounter; + boolean oldInMXMLContent; + + oldDescriptorTree = descriptorTree; + descriptorTree = new ArrayList<MXMLDescriptorSpecifier>(); + oldPropertiesTree = propertiesTree; + propertiesTree = new MXMLDescriptorSpecifier(); + + oldInMXMLContent = inMXMLContent; + inMXMLContent = false; + oldEvents = events; + events = new ArrayList<MXMLEventSpecifier>(); + oldInstances = instances; + instances = new ArrayList<MXMLDescriptorSpecifier>(); + oldScripts = scripts; + scripts = new ArrayList<MXMLScriptSpecifier>(); + //styles = new ArrayList<MXMLStyleSpecifier>(); + + oldCurrentInstances = currentInstances; + currentInstances = new ArrayList<MXMLDescriptorSpecifier>(); + oldCurrentPropertySpecifiers = currentPropertySpecifiers; + currentPropertySpecifiers = new ArrayList<MXMLDescriptorSpecifier>(); + + oldEventCounter = eventCounter; + eventCounter = 0; + oldIdCounter = idCounter; + idCounter = 0; + + // visit MXML + IClassDefinition oldClassDef = classDefinition; + IClassDefinition cdef = node.getContainedClassDefinition(); + classDefinition = cdef; + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + ((JSFlexJSEmitter) asEmitter).getModel().pushClass(cdef); + + IASNode classNode = node.getContainedClassDefinitionNode(); + // visit tags + final int len = classNode.getChildCount(); + for (int i = 0; i < len; i++) + { + getMXMLWalker().walk(classNode.getChild(i)); + } + + String cname = cdef.getQualifiedName(); + subDocumentNames.add(cname); + ((JSFlexJSEmitter) asEmitter).mxmlEmitter = this; + String baseClassName = cdef.getBaseClassAsDisplayString(); + + emitClassDeclStart(cname, baseClassName, false); + + emitComplexInitializers(classNode); + + emitPropertyDecls(); + + emitClassDeclEnd(cname, baseClassName); + + emitMetaData(cdef); + + emitScripts(); + + emitEvents(cname); + + emitPropertyGetterSetters(cname); + + emitMXMLDescriptorFuncs(cname); + + emitBindingData(cname, cdef); + + write(((JSFlexJSEmitter) asEmitter).stringifyDefineProperties(cdef)); + + descriptorTree = oldDescriptorTree; + propertiesTree = oldPropertiesTree; + events = oldEvents; + scripts = oldScripts; + currentInstances = oldCurrentInstances; + allInstances.addAll(instances); + instances = oldInstances; + currentPropertySpecifiers = oldCurrentPropertySpecifiers; + eventCounter = oldEventCounter; + idCounter = oldIdCounter; + inMXMLContent = oldInMXMLContent; + classDefinition = oldClassDef; + ((JSFlexJSEmitter) asEmitter).getModel().popClass(); + ((JSFlexJSEmitter) asEmitter).mxmlEmitter = null; + + } + + @Override + public void emitMetadata(IMXMLMetadataNode node) + { + metadataNodes.add(node); + } + + //-------------------------------------------------------------------------- + + protected void emitClassDeclStart(String cname, String baseClassName, + boolean indent) + { + writeNewline(); + writeNewline("/**"); + writeNewline(" * @constructor"); + writeNewline(" * @extends {" + formatQualifiedName(baseClassName) + "}"); + writeNewline(" */"); + writeToken(formatQualifiedName(cname)); + writeToken(ASEmitterTokens.EQUAL); + write(ASEmitterTokens.FUNCTION); + write(ASEmitterTokens.PAREN_OPEN); + writeToken(ASEmitterTokens.PAREN_CLOSE); + if (indent) + indentPush(); + writeNewline(ASEmitterTokens.BLOCK_OPEN, true); + write(formatQualifiedName(cname)); + write(ASEmitterTokens.MEMBER_ACCESS); + write(JSGoogEmitterTokens.GOOG_BASE); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.THIS); + writeToken(ASEmitterTokens.COMMA); + write(ASEmitterTokens.SINGLE_QUOTE); + write(JSGoogEmitterTokens.GOOG_CONSTRUCTOR); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + writeNewline(ASEmitterTokens.SEMICOLON); + } + + //-------------------------------------------------------------------------- + + protected void emitClassDeclEnd(String cname, String baseClassName) + { + writeNewline(); + writeNewline("/**"); + writeNewline(" * @private"); + writeNewline(" * @type {Array}"); + writeNewline(" */"); + writeNewline("this.mxmldd;"); + + // top level is 'mxmlContent', skip it... + if (currentStateOverrides.propertySpecifiers.size() > 0) + { + MXMLDescriptorSpecifier root = currentStateOverrides; + root.isTopNode = true; + + writeNewline("/**"); + writeNewline(" * @export"); + writeNewline(" * @type {Array}"); + writeNewline(" */"); + writeNewline("this.mxmlsd = " + ASEmitterTokens.SQUARE_OPEN.getToken()); + indentPush(); + write(root.outputStateDescriptors()); + write("null"); + write(ASEmitterTokens.SQUARE_CLOSE); + indentPop(); + writeNewline(ASEmitterTokens.SEMICOLON); + } + + writeNewline(); + writeNewline("/**"); + writeNewline(" * @private"); + writeNewline(" * @type {Array}"); + writeNewline(" */"); + + indentPop(); + writeNewline("this.mxmldp;"); + + if (propertiesTree.propertySpecifiers.size() > 0 || + propertiesTree.eventSpecifiers.size() > 0) + { + indentPush(); + writeNewline(); + writeNewline("this.generateMXMLAttributes"); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SQUARE_OPEN); + + MXMLDescriptorSpecifier root = propertiesTree; + root.isTopNode = true; + writeNewline(root.output(true)); + + write(ASEmitterTokens.SQUARE_CLOSE); + write(ASEmitterTokens.PAREN_CLOSE); + writeNewline(ASEmitterTokens.SEMICOLON); + indentPop(); + writeNewline(); + } + + write(ASEmitterTokens.BLOCK_CLOSE); + writeNewline(ASEmitterTokens.SEMICOLON); + write(JSGoogEmitterTokens.GOOG_INHERITS); + write(ASEmitterTokens.PAREN_OPEN); + write(formatQualifiedName(cname)); + writeToken(ASEmitterTokens.COMMA); + write(formatQualifiedName(baseClassName)); + write(ASEmitterTokens.PAREN_CLOSE); + writeNewline(ASEmitterTokens.SEMICOLON); + writeNewline(); + writeNewline(); + } + + //-------------------------------------------------------------------------- + + protected void emitMetaData(IClassDefinition cdef) + { + String cname = cdef.getQualifiedName(); + + writeNewline("/**"); + writeNewline(" * Metadata"); + writeNewline(" *"); + writeNewline(" * @type {Object.<string, Array.<Object>>}"); + writeNewline(" */"); + write(formatQualifiedName(cname) + ".prototype.FLEXJS_CLASS_INFO = { names: [{ name: '"); + write(cdef.getBaseName()); + write("', qName: '"); + write(formatQualifiedName(cname)); + write("' }]"); + if (interfaceList != null) + { + write(", interfaces: ["); + write(interfaceList); + write("]"); + } + write(" };"); + + writeNewline(); + writeNewline(); + writeNewline(); + writeNewline("/**"); + writeNewline(" * Prevent renaming of class. Needed for reflection."); + writeNewline(" */"); + write(JSFlexJSEmitterTokens.GOOG_EXPORT_SYMBOL); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write(formatQualifiedName(cname)); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.COMMA); + write(ASEmitterTokens.SPACE); + write(formatQualifiedName(cname)); + write(ASEmitterTokens.PAREN_CLOSE); + write(ASEmitterTokens.SEMICOLON); + + emitReflectionData(cdef); + writeNewline(); + writeNewline(); + + } + + private void emitReflectionData(IClassDefinition cdef) + { + JSFlexJSEmitter asEmitter = (JSFlexJSEmitter)((IMXMLBlockWalker) getMXMLWalker()).getASEmitter(); + + ArrayList<PackageFooterEmitter.VariableData> varData = new ArrayList<PackageFooterEmitter.VariableData>(); + // vars can only come from script blocks? + List<IVariableNode> vars = asEmitter.getModel().getVars(); + for (IVariableNode varNode : vars) + { + String ns = varNode.getNamespace(); + if (ns == IASKeywordConstants.PUBLIC) + { + PackageFooterEmitter.VariableData data = asEmitter.packageFooterEmitter.new VariableData(); + varData.add(data); + data.name = varNode.getName(); + data.type = formatQualifiedName(varNode.getVariableType()); + IMetaTagsNode metaData = varNode.getMetaTags(); + if (metaData != null) + { + IMetaTagNode[] tags = metaData.getAllTags(); + if (tags.length > 0) + data.metaData = tags; + } + } + } + + ArrayList<PackageFooterEmitter.MethodData> accessorData = new ArrayList<PackageFooterEmitter.MethodData>(); + HashMap<String, PropertyNodes> accessors = asEmitter.getModel().getPropertyMap(); + for (String propName : accessors.keySet()) + { + PropertyNodes p = accessors.get(propName); + IFunctionNode accessorNode = p.getter; + if (accessorNode == null) + accessorNode = p.setter; + String ns = accessorNode.getNamespace(); + if (ns == IASKeywordConstants.PUBLIC) + { + PackageFooterEmitter.MethodData data = asEmitter.packageFooterEmitter.new MethodData(); + accessorData.add(data); + data.name = accessorNode.getName(); + if (p.getter != null) + data.type = formatQualifiedName(p.getter.getReturnType()); + else + data.type = formatQualifiedName(p.setter.getVariableType()); + data.declaredBy = formatQualifiedName(cdef.getQualifiedName()); + IMetaTagsNode metaData = accessorNode.getMetaTags(); + if (metaData != null) + { + IMetaTagNode[] tags = metaData.getAllTags(); + if (tags.length > 0) + data.metaData = tags; + } + } + } + + for (MXMLDescriptorSpecifier instance : instances) + { + if (!instance.id.startsWith(MXMLFlexJSEmitterTokens.ID_PREFIX + .getToken())) + { + PackageFooterEmitter.MethodData data = asEmitter.packageFooterEmitter.new MethodData(); + accessorData.add(data); + data.name = instance.id; + data.type = formatQualifiedName(instance.name); + data.declaredBy = formatQualifiedName(cdef.getQualifiedName()); + } + } + + ArrayList<PackageFooterEmitter.MethodData> methodData = new ArrayList<PackageFooterEmitter.MethodData>(); + List<IFunctionNode> methods = asEmitter.getModel().getMethods(); + for (IFunctionNode methodNode : methods) + { + String ns = methodNode.getNamespace(); + if (ns == IASKeywordConstants.PUBLIC) + { + PackageFooterEmitter.MethodData data = asEmitter.packageFooterEmitter.new MethodData(); + methodData.add(data); + data.name = methodNode.getName(); + data.type = formatQualifiedName(methodNode.getReturnType()); + data.declaredBy = formatQualifiedName(cdef.getQualifiedName()); + IMetaTagsNode metaData = methodNode.getMetaTags(); + if (metaData != null) + { + IMetaTagNode[] tags = metaData.getAllTags(); + if (tags.length > 0) + data.metaData = tags; + } + } + } + + for (MXMLEventSpecifier event : events) + { + PackageFooterEmitter.MethodData data = asEmitter.packageFooterEmitter.new MethodData(); + methodData.add(data); + data.name = event.eventHandler; + data.type = ASEmitterTokens.VOID.getToken(); + data.declaredBy = formatQualifiedName(cdef.getQualifiedName()); + } + + ArrayList<IMetaTagNode> metadataTagNodes = new ArrayList<IMetaTagNode>(); + for (IMXMLMetadataNode metadataTag : metadataNodes) + { + IMetaTagNode[] tags = metadataTag.getMetaTagNodes(); + for (IMetaTagNode tag : tags) + { + metadataTagNodes.add(tag); + } + } + IMetaTagNode[] metaDataTags = new IMetaTagNode[metadataTagNodes.size()]; + asEmitter.packageFooterEmitter.emitReflectionData(formatQualifiedName(cdef.getQualifiedName()), varData, + accessorData, methodData, metadataTagNodes.toArray(metaDataTags)); + } + + //-------------------------------------------------------------------------- + + protected void emitPropertyDecls() + { + for (MXMLDescriptorSpecifier instance : instances) + { + writeNewline(); + writeNewline("/**"); + writeNewline(" * @private"); + writeNewline(" * @type {" + instance.name + "}"); + writeNewline(" */"); + write(ASEmitterTokens.THIS); + write(ASEmitterTokens.MEMBER_ACCESS); + write(instance.id + "_"); + writeNewline(ASEmitterTokens.SEMICOLON); + } + } + + //-------------------------------------------------------------------------- + + protected void emitBindingData(String cname, IClassDefinition cdef) + { + BindingDatabase bd = BindingDatabase.bindingMap.get(cdef); + if (bd == null) + return; + if (bd.getBindingInfo().isEmpty()) + return; + + outputBindingInfoAsData(cname, bd); + } + + private void outputBindingInfoAsData(String cname, BindingDatabase bindingDataBase) + { + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + + writeNewline("/**"); + writeNewline(" * @export"); + writeNewline(" */"); + writeNewline(formatQualifiedName(cname) + + ".prototype._bindings = ["); + + Set<BindingInfo> bindingInfo = bindingDataBase.getBindingInfo(); + writeNewline(bindingInfo.size() + ","); // number of bindings + + for (BindingInfo bi : bindingInfo) + { + String s; + s = bi.getSourceString(); + if (s == null) + s = getSourceStringFromGetter(bi.getExpressionNodesForGetter()); + if (s.contains(".")) + { + String[] parts = s.split("\\."); + write(ASEmitterTokens.SQUARE_OPEN.getToken() + ASEmitterTokens.DOUBLE_QUOTE.getToken() + + parts[0] + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + int n = parts.length; + for (int i = 1; i < n; i++) + { + String part = parts[i]; + write(", " + ASEmitterTokens.DOUBLE_QUOTE.getToken() + part + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + } + writeNewline(ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else if (s == null || s.length() == 0) + { + List<IExpressionNode> getterNodes = bi.getExpressionNodesForGetter(); + StringBuilder sb = new StringBuilder(); + sb.append("function() { return "); + int n = getterNodes.size(); + for (int i = 0; i < n; i++) + { + IExpressionNode getterNode = getterNodes.get(i); + if (getterNode.getNodeID() == ASTNodeID.LiteralStringID) + { + sb.append(ASEmitterTokens.DOUBLE_QUOTE.getToken()); + sb.append(asEmitter.stringifyNode(getterNode)); + sb.append(ASEmitterTokens.DOUBLE_QUOTE.getToken()); + } + else + sb.append(asEmitter.stringifyNode(getterNode)); + if (i < n - 1) + sb.append(ASEmitterTokens.SPACE.getToken() + ASEmitterTokens.PLUS.getToken() + ASEmitterTokens.SPACE.getToken()); + } + sb.append("; },"); + writeNewline(sb.toString()); + } + else + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + s + + ASEmitterTokens.DOUBLE_QUOTE.getToken() + ASEmitterTokens.COMMA.getToken()); + + IExpressionNode destNode = bi.getExpressionNodeForDestination(); + if (destNode != null) + { + StringBuilder sb = new StringBuilder(); + sb.append(generateSetterFunction(destNode)); + writeNewline(sb.toString() + ASEmitterTokens.COMMA.getToken()); + } + else + writeNewline(ASEmitterTokens.NULL.getToken() + ASEmitterTokens.COMMA.getToken()); + + s = bi.getDestinationString(); + if (s == null) + { + writeNewline(ASEmitterTokens.NULL.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else if (s.contains(".")) + { + String[] parts = s.split("\\."); + write(ASEmitterTokens.SQUARE_OPEN.getToken() + ASEmitterTokens.DOUBLE_QUOTE.getToken() + + parts[0] + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + int n = parts.length; + for (int i = 1; i < n; i++) + { + String part = parts[i]; + write(", " + ASEmitterTokens.DOUBLE_QUOTE.getToken() + part + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + } + writeNewline(ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + s + + ASEmitterTokens.DOUBLE_QUOTE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + Set<Entry<Object, WatcherInfoBase>> watcherChains = bindingDataBase.getWatcherChains(); + if (watcherChains != null) + { + for (Entry<Object, WatcherInfoBase> entry : watcherChains) + { + WatcherInfoBase watcherInfoBase = entry.getValue(); + encodeWatcher(watcherInfoBase); + } + } + // add a trailing null for now so I don't have to have logic where the watcher figures out not to add + // a comma + writeNewline("null" + ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.SEMICOLON.getToken()); + } + + private String generateSetterFunction(IExpressionNode destNode) { + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + String body = asEmitter.stringifyNode(destNode); + + StringBuilder sb = new StringBuilder(); + sb.append("function (value) { "); + int lastGet = body.lastIndexOf("get_"); + int lastDot = body.lastIndexOf("."); + if (lastDot == lastGet - 1) + { + String object = body.substring(0, lastDot); + String getter = body.substring(lastDot); + String setter = getter.replace("get_", "set_"); + setter = setter.replace("()", "(value)"); + body = object + setter; + sb.append(body); + } + else + { + sb.append(body); + sb.append(" = value;"); + } + sb.append(";}"); + return sb.toString(); + } + + private void encodeWatcher(WatcherInfoBase watcherInfoBase) + { + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + + writeNewline(watcherInfoBase.getIndex() + ASEmitterTokens.COMMA.getToken()); + WatcherType type = watcherInfoBase.getType(); + if (type == WatcherType.FUNCTION) + { + writeNewline("0" + ASEmitterTokens.COMMA.getToken()); + + FunctionWatcherInfo functionWatcherInfo = (FunctionWatcherInfo)watcherInfoBase; + + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + functionWatcherInfo.getFunctionName() + + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + IExpressionNode params[] = functionWatcherInfo.params; + StringBuilder sb = new StringBuilder(); + sb.append("function() { return ["); + boolean firstone = true; + for (IExpressionNode param : params) + { + if (firstone) + firstone = false; + sb.append(ASEmitterTokens.COMMA.getToken()); + sb.append(asEmitter.stringifyNode(param)); + } + sb.append("]; },"); + outputEventNames(functionWatcherInfo.getEventNames()); + outputBindings(functionWatcherInfo.getBindings()); + } + else if ((type == WatcherType.STATIC_PROPERTY) || (type == WatcherType.PROPERTY)) + { + writeNewline((type == WatcherType.STATIC_PROPERTY ? "1" : "2") + + ASEmitterTokens.COMMA.getToken()); + + PropertyWatcherInfo propertyWatcherInfo = (PropertyWatcherInfo)watcherInfoBase; + + boolean makeStaticWatcher = (watcherInfoBase.getType() == WatcherType.STATIC_PROPERTY); + + // round up the getter function for the watcher, or null if we don't need one + MethodInfo propertyGetterFunction = null; + if (watcherInfoBase.isRoot && !makeStaticWatcher) + { + // TODO: figure out what this looks like + // propertyGetterFunction = this.propertyGetter; + // assert propertyGetterFunction != null; + } + else if (watcherInfoBase.isRoot && makeStaticWatcher) + { + // TODO: implement getter func for static watcher. + } + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + propertyWatcherInfo.getPropertyName() + + ASEmitterTokens.DOUBLE_QUOTE.getToken() + ASEmitterTokens.COMMA.getToken()); + outputEventNames(propertyWatcherInfo.getEventNames()); + outputBindings(propertyWatcherInfo.getBindings()); + if (propertyGetterFunction == null) + writeNewline("null" + ASEmitterTokens.COMMA.getToken()); // null is valid + if (type == WatcherType.STATIC_PROPERTY) + { + StaticPropertyWatcherInfo pwinfo = (StaticPropertyWatcherInfo)watcherInfoBase; + Name classMName = pwinfo.getContainingClass(getMXMLWalker().getProject()); + writeNewline(nameToString(classMName)); + } + } + else if (type == WatcherType.XML) + { + writeNewline("3" + ASEmitterTokens.COMMA.getToken()); + + XMLWatcherInfo xmlWatcherInfo = (XMLWatcherInfo)watcherInfoBase; + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + xmlWatcherInfo.getPropertyName() + + ASEmitterTokens.DOUBLE_QUOTE.getToken() + ASEmitterTokens.COMMA.getToken()); + outputBindings(xmlWatcherInfo.getBindings()); + } + else assert false; + + // then recurse into children + Set<Entry<Object, WatcherInfoBase>> children = watcherInfoBase.getChildren(); + if (children != null) + { + writeNewline(ASEmitterTokens.SQUARE_OPEN.getToken()); + for ( Entry<Object, WatcherInfoBase> ent : children) + { + encodeWatcher(ent.getValue()); + } + writeNewline("null" + ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else + { + writeNewline("null" + ASEmitterTokens.COMMA.getToken()); + } + } + + private String getSourceStringFromMemberAccessExpressionNode(MemberAccessExpressionNode node) + { + String s = ""; + + IExpressionNode left = node.getLeftOperandNode(); + if (left instanceof FunctionCallNode) // probably a cast + { + IASNode child = ((FunctionCallNode)left).getArgumentsNode().getChild(0); + if (child instanceof IdentifierNode) + s = getSourceStringFromIdentifierNode((IdentifierNode)child); + else if (child instanceof MemberAccessExpressionNode) + s = getSourceStringFromMemberAccessExpressionNode((MemberAccessExpressionNode)child); + } + else if (left instanceof MemberAccessExpressionNode) + s = getSourceStringFromMemberAccessExpressionNode((MemberAccessExpressionNode)left); + else if (left instanceof IdentifierNode) + s = getSourceStringFromIdentifierNode((IdentifierNode)left); + else + System.out.println("expected binding member access left node" + node.toString()); + s += "."; + + IExpressionNode right = node.getRightOperandNode(); + if (right instanceof FunctionCallNode) // probably a cast + { + IASNode child = ((FunctionCallNode)right).getArgumentsNode().getChild(0); + if (child instanceof IdentifierNode) + s += getSourceStringFromIdentifierNode((IdentifierNode)child); + else if (child instanceof MemberAccessExpressionNode) + s += getSourceStringFromMemberAccessExpressionNode((MemberAccessExpressionNode)child); + } + else if (right instanceof MemberAccessExpressionNode) + s += getSourceStringFromMemberAccessExpressionNode((MemberAccessExpressionNode)right); + else if (right instanceof IdentifierNode) + s += getSourceStringFromIdentifierNode((IdentifierNode)right); + else + System.out.println("expected binding member access right node" + node.toString()); + + return s; + } + + private String getSourceStringFromIdentifierNode(IdentifierNode node) + { + return node.getName(); + } + + private String getSourceStringFromGetter(List<IExpressionNode> nodes) + { + String s = ""; + IExpressionNode node = nodes.get(0); + if (node instanceof MemberAccessExpressionNode) + { + s = getSourceStringFromMemberAccessExpressionNode((MemberAccessExpressionNode)node); + } + else if (node instanceof IdentifierNode) + { + s = ((IdentifierNode)node).getName(); + } + return s; + } + + private void outputEventNames(List<String> events) + { + if (events.size() > 1) + { + int n = events.size(); + write(ASEmitterTokens.SQUARE_OPEN.getToken() + ASEmitterTokens.DOUBLE_QUOTE.getToken() + + events.get(0) + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + for (int i = 1; i < n; i++) + { + String event = events.get(i); + write(ASEmitterTokens.COMMA.getToken() + ASEmitterTokens.DOUBLE_QUOTE.getToken() + + event + ASEmitterTokens.DOUBLE_QUOTE.getToken()); + } + writeNewline(ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else if (events.size() == 1) + writeNewline(ASEmitterTokens.DOUBLE_QUOTE.getToken() + events.get(0) + + ASEmitterTokens.DOUBLE_QUOTE.getToken() + ASEmitterTokens.COMMA.getToken()); + else + writeNewline("null" + ASEmitterTokens.COMMA.getToken()); + } + + private void outputBindings(List<BindingInfo> bindings) + { + if (bindings.size() > 1) + { + int n = bindings.size(); + write(ASEmitterTokens.SQUARE_OPEN.getToken() + bindings.get(0).getIndex()); + for (int i = 1; i < n; i++) + { + BindingInfo binding = bindings.get(i); + write(ASEmitterTokens.COMMA.getToken() + binding.getIndex()); + } + writeNewline(ASEmitterTokens.SQUARE_CLOSE.getToken() + ASEmitterTokens.COMMA.getToken()); + } + else if (bindings.size() == 1) + writeNewline(bindings.get(0).getIndex() + ASEmitterTokens.COMMA.getToken()); + else + writeNewline("null" + ASEmitterTokens.COMMA.getToken()); + + } + + //-------------------------------------------------------------------------- + + protected void emitScripts() + { + for (MXMLScriptSpecifier script : scripts) + { + String output = script.output(); + + if (!output.equals("")) + { + writeNewline(output); + } + } + } + + //-------------------------------------------------------------------------- + + protected void emitEvents(String cname) + { + for (MXMLEventSpecifier event : events) + { + writeNewline("/**"); + writeNewline(" * @export"); + writeNewline(" * @param {" + formatQualifiedName(event.type) + "} event"); + writeNewline(" */"); + writeNewline(formatQualifiedName(cname) + + ".prototype." + event.eventHandler + " = function(event)"); + writeNewline(ASEmitterTokens.BLOCK_OPEN, true); + + writeNewline(event.value + ASEmitterTokens.SEMICOLON.getToken(), + false); + + write(ASEmitterTokens.BLOCK_CLOSE); + writeNewline(";"); + writeNewline(); + writeNewline(); + } + } + + //-------------------------------------------------------------------------- + + protected void emitPropertyGetterSetters(String cname) + { + int n = 0; + for (MXMLDescriptorSpecifier instance : instances) + { + if (!instance.id.startsWith(MXMLFlexJSEmitterTokens.ID_PREFIX + .getToken())) + { + n++; + } + } + if (n == 0 && descriptorTree.size() == 0) + return; + + String formattedCName = formatQualifiedName(cname); + + write("Object.defineProperties("); + write(formattedCName); + writeNewline(".prototype, /** @lends {" + formattedCName + ".prototype} */ {"); + indentPush(); + int i = 0; + for (MXMLDescriptorSpecifier instance : instances) + { + if (!instance.id.startsWith(MXMLFlexJSEmitterTokens.ID_PREFIX + .getToken())) + { + indentPush(); + writeNewline("/** @export */"); + writeNewline(instance.id + ": {"); + writeNewline("/** @this {" + formattedCName + "} */"); + indentPush(); + writeNewline("get: function() {"); + indentPop(); + writeNewline("return this." + instance.id + "_;"); + writeNewline("},"); + writeNewline("/** @this {" + formattedCName + "} */"); + indentPush(); + writeNewline("set: function(value) {"); + indentPush(); + writeNewline("if (value != this." + instance.id + "_) {"); + writeNewline("this." + instance.id + "_ = value;"); + write("this.dispatchEvent(org.apache.flex.events.ValueChangeEvent.createUpdateEvent(this, '"); + indentPop(); + writeNewline(instance.id + "', null, value));"); + indentPop(); + writeNewline("}"); + indentPop(); + writeNewline("}"); + if (i < n - 1 || descriptorTree.size() > 0) + writeNewline("},"); + else + { + indentPop(); + writeNewline("}"); + } + i++; + } + } + if (descriptorTree.size() == 0) + writeNewline("});"); + } + + //-------------------------------------------------------------------------- + + protected void emitMXMLDescriptorFuncs(String cname) + { + // top level is 'mxmlContent', skip it... + if (descriptorTree.size() > 0) + { + FlexJSProject project = (FlexJSProject) getMXMLWalker().getProject(); + project.needLanguage = true; + MXMLDescriptorSpecifier root = descriptorTree.get(0); + root.isTopNode = false; + + indentPush(); + writeNewline("'MXMLDescriptor': {"); + writeNewline("/** @this {" + formatQualifiedName(cname) + "} */"); + indentPush(); + writeNewline("get: function() {"); + indentPush(); + writeNewline("{"); + writeNewline("if (this.mxmldd == undefined)"); + indentPush(); + writeNewline("{"); + writeNewline("/** @type {Array} */"); + writeNewline("var arr = org.apache.flex.utils.Language.superGetter(" + formatQualifiedName(cname) + ",this, 'MXMLDescriptor');"); + writeNewline("/** @type {Array} */"); + indentPop(); + indentPop(); + writeNewline("var data = ["); + + writeNewline(root.output(true)); + + indentPush(); + writeNewline("];"); + indentPush(); + writeNewline(""); + indentPush(); + writeNewline("if (arr)"); + indentPop(); + writeNewline("this.mxmldd = arr.concat(data);"); + indentPush(); + writeNewline("else"); + indentPop(); + indentPop(); + writeNewline("this.mxmldd = data;"); + writeNewline("}"); + indentPop(); + writeNewline("return this.mxmldd;"); + writeNewline("}"); + indentPop(); + writeNewline("}"); + indentPop(); + writeNewline("}"); + writeNewline("});"); + } + + } + + //-------------------------------------------------------------------------- + + private HashMap<IMXMLEventSpecifierNode, String> eventHandlerNameMap = new HashMap<IMXMLEventSpecifierNode, String>(); + + @Override + public void emitEventSpecifier(IMXMLEventSpecifierNode node) + { + if (isStateDependent(node) && !inStatesOverride) + return; + + IDefinition cdef = node.getDefinition(); + + MXMLDescriptorSpecifier currentDescriptor = getCurrentDescriptor("i"); + + MXMLEventSpecifier eventSpecifier = new MXMLEventSpecifier(); + eventSpecifier.eventHandler = MXMLFlexJSEmitterTokens.EVENT_PREFIX + .getToken() + eventCounter++; + eventSpecifier.name = cdef.getBaseName(); + eventSpecifier.type = node.getEventParameterDefinition() + .getTypeAsDisplayString(); + + eventHandlerNameMap.put(node, eventSpecifier.eventHandler); + + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + + StringBuilder sb = null; + int len = node.getChildCount(); + if (len > 0) + { + sb = new StringBuilder(); + for (int i = 0; i < len; i++) + { + sb.append(getIndent((i > 0) ? 1 : 0) + + asEmitter.stringifyNode(node.getChild(i))); + if (i < len - 1) + { + sb.append(ASEmitterTokens.SEMICOLON.getToken()); + sb.append(ASEmitterTokens.NEW_LINE.getToken()); + } + } + } + eventSpecifier.value = sb.toString(); + + if (currentDescriptor != null) + currentDescriptor.eventSpecifiers.add(eventSpecifier); + else if (!inStatesOverride) // in theory, if no currentdescriptor must be top tag event + propertiesTree.eventSpecifiers.add(eventSpecifier); + events.add(eventSpecifier); + } + + @Override + public void emitInstance(IMXMLInstanceNode node) + { + if (isStateDependent(node) && !inStatesOverride) + return; + + IClassDefinition cdef = node + .getClassReference((ICompilerProject) getMXMLWalker() + .getProject()); + + MXMLDescriptorSpecifier currentPropertySpecifier = getCurrentDescriptor("ps"); + + String id = node.getID(); + if (id == null) + id = node.getEffectiveID(); + if (id == null) + id = MXMLFlexJSEmitterTokens.ID_PREFIX.getToken() + idCounter++; + + MXMLDescriptorSpecifier currentInstance = new MXMLDescriptorSpecifier(); + currentInstance.isProperty = false; + currentInstance.id = id; + currentInstance.name = formatQualifiedName(cdef.getQualifiedName()); + currentInstance.parent = currentPropertySpecifier; + + if (currentPropertySpecifier != null) + currentPropertySpecifier.propertySpecifiers.add(currentInstance); + else if (inMXMLContent) + descriptorTree.add(currentInstance); + else + { + currentInstance.parent = propertiesTree; + propertiesTree.propertySpecifiers.add(currentInstance); + } + + instances.add(currentInstance); + + IMXMLPropertySpecifierNode[] pnodes = node.getPropertySpecifierNodes(); + if (pnodes != null) + { + moveDown(false, currentInstance, null); + + for (IMXMLPropertySpecifierNode pnode : pnodes) + { + getMXMLWalker().walk(pnode); // Property Specifier + } + + moveUp(false, true); + } + else if (node instanceof IMXMLStateNode) + { + IMXMLStateNode stateNode = (IMXMLStateNode)node; + String name = stateNode.getStateName(); + if (name != null) + { + MXMLDescriptorSpecifier stateName = new MXMLDescriptorSpecifier(); + stateName.isProperty = true; + stateName.id = id; + stateName.name = "name"; + stateName.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + name + ASEmitterTokens.SINGLE_QUOTE.getToken(); + stateName.parent = currentInstance; + currentInstance.propertySpecifiers.add(stateName); + } + MXMLDescriptorSpecifier overrides = new MXMLDescriptorSpecifier(); + overrides.isProperty = true; + overrides.hasArray = true; + overrides.id = id; + overrides.name = "overrides"; + overrides.parent = currentInstance; + currentInstance.propertySpecifiers.add(overrides); + moveDown(false, null, overrides); + + IMXMLClassDefinitionNode classDefinitionNode = stateNode.getClassDefinitionNode(); + List<IMXMLNode> snodes = classDefinitionNode.getNodesDependentOnState(stateNode.getStateName()); + if (snodes != null) + { + for (int i=snodes.size()-1; i>=0; --i) + { + IMXMLNode inode = snodes.get(i); + if (inode.getNodeID() == ASTNodeID.MXMLInstanceID) + { + emitInstanceOverride((IMXMLInstanceNode)inode); + } + } + // Next process the non-instance overrides dependent on this state. + // Each one will generate code to push an IOverride instance. + for (IMXMLNode anode : snodes) + { + switch (anode.getNodeID()) + { + case MXMLPropertySpecifierID: + { + emitPropertyOverride((IMXMLPropertySpecifierNode)anode); + break; + } + case MXMLStyleSpecifierID: + { + emitStyleOverride((IMXMLStyleSpecifierNode)anode); + break; + } + case MXMLEventSpecifierID: + { + emitEventOverride((IMXMLEventSpecifierNode)anode); + break; + } + default: + { + break; + } + } + } + } + + moveUp(false, false); + } + + IMXMLEventSpecifierNode[] enodes = node.getEventSpecifierNodes(); + if (enodes != null) + { + moveDown(false, currentInstance, null); + + for (IMXMLEventSpecifierNode enode : enodes) + { + getMXMLWalker().walk(enode); // Event Specifier + } + + moveUp(false, true); + } + } + + public void emitPropertyOverride(IMXMLPropertySpecifierNode propertyNode) + { + FlexProject project = (FlexProject) getMXMLWalker().getProject(); + Name propertyOverride = project.getPropertyOverrideClassName(); + emitPropertyOrStyleOverride(propertyOverride, propertyNode); + } + + /** + * Generates instructions in the current context + * to create an instance of mx.states.SetStyle + * with its <code>target</code>, <code>name</code>, + * and <code>value</code> properties set. + */ + void emitStyleOverride(IMXMLStyleSpecifierNode styleNode) + { + FlexProject project = (FlexProject) getMXMLWalker().getProject(); + Name styleOverride = project.getStyleOverrideClassName(); + emitPropertyOrStyleOverride(styleOverride, styleNode); + } + + void emitPropertyOrStyleOverride(Name overrideName, IMXMLPropertySpecifierNode propertyOrStyleNode) + { + MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("ps"); + IASNode parentNode = propertyOrStyleNode.getParent(); + String id = parentNode instanceof IMXMLInstanceNode ? + ((IMXMLInstanceNode)parentNode).getEffectiveID() : + null; + + String name = propertyOrStyleNode.getName(); + + boolean valueIsDataBound = isDataBindingNode(propertyOrStyleNode.getChild(0)); + IMXMLInstanceNode propertyOrStyleValueNode = propertyOrStyleNode.getInstanceNode(); + + MXMLDescriptorSpecifier setProp = new MXMLDescriptorSpecifier(); + setProp.isProperty = false; + setProp.name = formatQualifiedName(nameToString(overrideName)); + setProp.parent = currentInstance; + currentInstance.propertySpecifiers.add(setProp); + + if (id != null) + { + // Set its 'target' property to the id of the object + // whose property or style this override will set. + MXMLDescriptorSpecifier target = new MXMLDescriptorSpecifier(); + target.isProperty = true; + target.name = "target"; + target.parent = setProp; + target.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + id + ASEmitterTokens.SINGLE_QUOTE.getToken(); + setProp.propertySpecifiers.add(target); + } + + // Set its 'name' property to the name of the property or style. + MXMLDescriptorSpecifier pname = new MXMLDescriptorSpecifier(); + pname.isProperty = true; + pname.name = "name"; + pname.parent = setProp; + pname.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + name + ASEmitterTokens.SINGLE_QUOTE.getToken(); + setProp.propertySpecifiers.add(pname); + + if (!valueIsDataBound) + { + // Set its 'value' property to the value of the property or style. + MXMLDescriptorSpecifier value = new MXMLDescriptorSpecifier(); + value.isProperty = true; + value.name = "value"; + value.parent = setProp; + setProp.propertySpecifiers.add(value); + moveDown(false, null, value); + getMXMLWalker().walk(propertyOrStyleValueNode); // instance node + moveUp(false, false); + } + else + { + String overrideID = MXMLFlexJSEmitterTokens.BINDING_PREFIX.getToken() + bindingCounter++; + setProp.id = overrideID; + instances.add(setProp); + BindingDatabase bd = BindingDatabase.bindingMap.get(classDefinition); + Set<BindingInfo> bindingInfo = bd.getBindingInfo(); + IMXMLDataBindingNode bindingNode = (IMXMLDataBindingNode)propertyOrStyleNode.getChild(0); + for (BindingInfo bi : bindingInfo) + { + if (bi.node == bindingNode) + { + bi.setDestinationString(overrideID + ".value"); + break; + } + } + } + } + + /** + * Generates instructions in the current context + * to create an instance of mx.states.SetEventHandler + * with its <code>target</code>, <code>name</code>, + * and <code>handlerFunction</code> properties set. + */ + void emitEventOverride(IMXMLEventSpecifierNode eventNode) + { + inStatesOverride = true; + + MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("ps"); + FlexProject project = (FlexProject) getMXMLWalker().getProject(); + Name eventOverride = project.getEventOverrideClassName(); + + IASNode parentNode = eventNode.getParent(); + String id = parentNode instanceof IMXMLInstanceNode ? + ((IMXMLInstanceNode)parentNode).getEffectiveID() : + ""; + + String name = MXMLEventSpecifier.getJSEventName(eventNode.getName()); + + String eventHandler = eventHandlerNameMap.get(eventNode); + if (eventHandler == null) + { + emitEventSpecifier(eventNode); + eventHandler = eventHandlerNameMap.get(eventNode); + } + + MXMLDescriptorSpecifier setEvent = new MXMLDescriptorSpecifier(); + setEvent.isProperty = false; + setEvent.name = formatQualifiedName(nameToString(eventOverride)); + setEvent.parent = currentInstance; + currentInstance.propertySpecifiers.add(setEvent); + // Set its 'target' property to the id of the object + // whose event this override will set. + MXMLDescriptorSpecifier target = new MXMLDescriptorSpecifier(); + target.isProperty = true; + target.name = "target"; + target.parent = setEvent; + target.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + id + ASEmitterTokens.SINGLE_QUOTE.getToken(); + setEvent.propertySpecifiers.add(target); + + // Set its 'name' property to the name of the event. + MXMLDescriptorSpecifier pname = new MXMLDescriptorSpecifier(); + pname.isProperty = true; + pname.name = "name"; + pname.parent = setEvent; + pname.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + name + ASEmitterTokens.SINGLE_QUOTE.getToken(); + setEvent.propertySpecifiers.add(pname); + + // Set its 'handlerFunction' property to the autogenerated event handler. + MXMLDescriptorSpecifier handler = new MXMLDescriptorSpecifier(); + handler.isProperty = true; + handler.name = "handlerFunction"; + handler.parent = setEvent; + handler.value = JSFlexJSEmitterTokens.CLOSURE_FUNCTION_NAME.getToken() + ASEmitterTokens.PAREN_OPEN.getToken() + + ASEmitterTokens.THIS.getToken() + ASEmitterTokens.MEMBER_ACCESS.getToken() + eventHandler + + ASEmitterTokens.COMMA.getToken() + ASEmitterTokens.SPACE.getToken() + ASEmitterTokens.THIS.getToken() + + ASEmitterTokens.COMMA.getToken() + ASEmitterTokens.SPACE.getToken() + ASEmitterTokens.SINGLE_QUOTE.getToken() + + eventHandler + ASEmitterTokens.SINGLE_QUOTE.getToken() + + ASEmitterTokens.PAREN_CLOSE.getToken(); + setEvent.propertySpecifiers.add(handler); + + inStatesOverride = false; + } + + public void emitInstanceOverride(IMXMLInstanceNode instanceNode) + { + inStatesOverride = true; + + MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("ps"); + FlexProject project = (FlexProject) getMXMLWalker().getProject(); + Name instanceOverrideName = project.getInstanceOverrideClassName(); + + MXMLDescriptorSpecifier overrideInstances = getCurrentDescriptor("so"); + int index = overrideInstances.propertySpecifiers.size(); + if (nodeToIndexMap == null) + nodeToIndexMap = new HashMap<IMXMLNode, Integer>(); + if (nodeToIndexMap.containsKey(instanceNode)) + { + index = nodeToIndexMap.get(instanceNode); + } + else + { + nodeToIndexMap.put(instanceNode, index); + MXMLDescriptorSpecifier itemsDesc = new MXMLDescriptorSpecifier(); + itemsDesc.isProperty = true; + itemsDesc.hasArray = true; + itemsDesc.name = "itemsDescriptor"; + itemsDesc.parent = overrideInstances; + overrideInstances.propertySpecifiers.add(itemsDesc); + boolean oldInMXMLContent = inMXMLContent; + moveDown(false, null, itemsDesc); + inMXMLContent = true; + getMXMLWalker().walk(instanceNode); // instance node + inMXMLContent = oldInMXMLContent; + moveUp(false, false); + } + + MXMLDescriptorSpecifier addItems = new MXMLDescriptorSpecifier(); + addItems.isProperty = false; + addItems.name = formatQualifiedName(nameToString(instanceOverrideName)); + addItems.parent = currentInstance; + currentInstance.propertySpecifiers.add(addItems); + MXMLDescriptorSpecifier itemsDescIndex = new MXMLDescriptorSpecifier(); + itemsDescIndex.isProperty = true; + itemsDescIndex.hasArray = true; + itemsDescIndex.name = "itemsDescriptorIndex"; + itemsDescIndex.parent = addItems; + itemsDescIndex.value = Integer.toString(index); + addItems.propertySpecifiers.add(itemsDescIndex); + + //----------------------------------------------------------------------------- + // Second property set: maybe set destination and propertyName + + // get the property specifier node for the property the instanceNode represents + IMXMLPropertySpecifierNode propertySpecifier = (IMXMLPropertySpecifierNode) + instanceNode.getAncestorOfType( IMXMLPropertySpecifierNode.class); + + if (propertySpecifier == null) + { + assert false; // I think this indicates an invalid tree... + } + else + { + // Check the parent - if it's an instance then we want to use these + // nodes to get our property values from. If not, then it's the root + // and we don't need to specify destination + + IASNode parent = propertySpecifier.getParent(); + if (parent instanceof IMXMLInstanceNode) + { + IMXMLInstanceNode parentInstance = (IMXMLInstanceNode)parent; + String parentId = parentInstance.getEffectiveID(); + assert parentId != null; + String propName = propertySpecifier.getName(); + + MXMLDescriptorSpecifier dest = new MXMLDescriptorSpecifier(); + dest.isProperty = true; + dest.name = "destination"; + dest.parent = addItems; + dest.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + parentId + ASEmitterTokens.SINGLE_QUOTE.getToken(); + addItems.propertySpecifiers.add(dest); + + MXMLDescriptorSpecifier prop = new MXMLDescriptorSpecifier(); + prop.isProperty = true; + prop.name = "propertyName"; + prop.parent = addItems; + prop.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + propName + ASEmitterTokens.SINGLE_QUOTE.getToken(); + addItems.propertySpecifiers.add(prop); + } + } + + //--------------------------------------------------------------- + // Third property set: position and relativeTo + String positionPropertyValue = null; + String relativeToPropertyValue = null; + + // look to see if we have any sibling nodes that are not state dependent + // that come BEFORE us + IASNode instanceParent = instanceNode.getParent(); + IASNode prevStatelessSibling=null; + for (int i=0; i< instanceParent.getChildCount(); ++i) + { + IASNode sib = instanceParent.getChild(i); + assert sib instanceof IMXMLInstanceNode; // surely our siblings are also instances? + + // stop looking for previous nodes when we find ourself + if (sib == instanceNode) + break; + + if (sib instanceof IMXMLInstanceNode && !isStateDependent(sib)) + { + prevStatelessSibling = sib; + } + } + + if (prevStatelessSibling == null) { + positionPropertyValue = "first"; // TODO: these should be named constants + } + else { + positionPropertyValue = "after"; + relativeToPropertyValue = ((IMXMLInstanceNode)prevStatelessSibling).getEffectiveID(); + } + + MXMLDescriptorSpecifier pos = new MXMLDescriptorSpecifier(); + pos.isProperty = true; + pos.name = "position"; + pos.parent = addItems; + pos.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + positionPropertyValue + ASEmitterTokens.SINGLE_QUOTE.getToken(); + addItems.propertySpecifiers.add(pos); + + if (relativeToPropertyValue != null) + { + MXMLDescriptorSpecifier rel = new MXMLDescriptorSpecifier(); + rel.isProperty = true; + rel.name = "relativeTo"; + rel.parent = addItems; + rel.value = ASEmitterTokens.SINGLE_QUOTE.getToken() + relativeToPropertyValue + ASEmitterTokens.SINGLE_QUOTE.getToken(); + addItems.propertySpecifiers.add(rel); + } + + inStatesOverride = false; + } + + private String nameToString(Name name) + { + String s = ""; + Namespace ns = name.getSingleQualifier(); + s = ns.getName() + ASEmitterTokens.MEMBER_ACCESS.getToken() + name.getBaseName(); + return s; + } + /** + * Determines whether a node is state-dependent. + * TODO: we should move to IMXMLNode + */ + protected boolean isStateDependent(IASNode node) + { + if (node instanceof IMXMLSpecifierNode) + { + String suffix = ((IMXMLSpecifierNode)node).getSuffix(); + return suffix != null && suffix.length() > 0; + } + else if (isStateDependentInstance(node)) + return true; + return false; + } + + /** + * Determines whether the geven node is an instance node, as is state dependent + */ + protected boolean isStateDependentInstance(IASNode node) + { + if (node instanceof IMXMLInstanceNode) + { + String[] includeIn = ((IMXMLInstanceNode)node).getIncludeIn(); + String[] excludeFrom = ((IMXMLInstanceNode)node).getExcludeFrom(); + return includeIn != null || excludeFrom != null; + } + return false; + } + + /** + * Is a give node a "databinding node"? + */ + public static boolean isDataBindingNode(IASNode node) + { + return node instanceof IMXMLDataBindingNode; + } + + protected static boolean isDataboundProp(IMXMLPropertySpecifierNode propertyNode) + { + boolean ret = propertyNode.getChildCount() > 0 && isDataBindingNode(propertyNode.getInstanceNode()); + + // Sanity check that we based our conclusion about databinding on the correct node. + // (code assumes only one child if databinding) + int n = propertyNode.getChildCount(); + for (int i = 0; i < n; i++) + { + boolean db = isDataBindingNode(propertyNode.getChild(i)); + assert db == ret; + } + + return ret; + } + + @Override + public void emitPropertySpecifier(IMXMLPropertySpecifierNode node) + { + if (isDataboundProp(node)) + return; + + if (isStateDependent(node)) + return; + + IDefinition cdef = node.getDefinition(); + + IASNode cnode = node.getChild(0); + + MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i"); + + MXMLDescriptorSpecifier currentPropertySpecifier = new MXMLDescriptorSpecifier(); + currentPropertySpecifier.isProperty = true; + currentPropertySpecifier.name = cdef.getQualifiedName(); + currentPropertySpecifier.parent = currentInstance; + + boolean oldInMXMLContent = inMXMLContent; + boolean reusingDescriptor = false; + if (currentPropertySpecifier.name.equals("mxmlContent")) + { + inMXMLContent = true; + ArrayList<MXMLDescriptorSpecifier> specList = + (currentInstance == null) ? descriptorTree : currentInstance.propertySpecifiers; + for (MXMLDescriptorSpecifier ds : specList) + { + if (ds.name.equals("mxmlContent")) + { + currentPropertySpecifier = ds; + reusingDescriptor = true; + break; + } + } + } + + if (currentInstance != null) + { + // we end up here for children of tags + if (!reusingDescriptor) + currentInstance.propertySpecifiers.add(currentPropertySpecifier); + } + else if (inMXMLContent) + { + // we end up here for top tags? + if (!reusingDescriptor) + descriptorTree.add(currentPropertySpecifier); + } + else + { + currentPropertySpecifier.parent = propertiesTree; + propertiesTree.propertySpecifiers.add(currentPropertySpecifier); + } + + boolean valueIsArray = cnode != null && cnode instanceof IMXMLArrayNode; + boolean valueIsObject = cnode != null && cnode instanceof IMXMLObjectNode; + + currentPropertySpecifier.hasArray = valueIsArray; + currentPropertySpecifier.hasObject = valueIsObject; + + moveDown(valueIsArray || valueIsObject, null, currentPropertySpecifier); + + getMXMLWalker().walk(cnode); // Array or Instance + + moveUp(valueIsArray || valueIsObject, false); + + inMXMLContent = oldInMXMLContent; + } + + @Override + public void emitScript(IMXMLScriptNode node) + { + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()) + .getASEmitter(); + + String nl = ASEmitterTokens.NEW_LINE.getToken(); + + StringBuilder sb = null; + MXMLScriptSpecifier scriptSpecifier = null; + + int len = node.getChildCount(); + if (len > 0) + { + for (int i = 0; i < len; i++) + { + IASNode cnode = node.getChild(i); + + if (!(cnode instanceof IImportNode)) + { + sb = new StringBuilder(); + scriptSpecifier = new MXMLScriptSpecifier(); + + sb.append(asEmitter.stringifyNode(cnode)); + + sb.append(ASEmitterTokens.SEMICOLON.getToken()); + + if (i == len - 1) + indentPop(); + + sb.append(nl); + sb.append(nl); + + scriptSpecifier.fragment = sb.toString(); + + scripts.add(scriptSpecifier); + } + } + } + } + + @Override + public void emitStyleSpecifier(IMXMLStyleSpecifierNode node) + { + } + + //-------------------------------------------------------------------------- + + @Override + public void emitObject(IMXMLObjectNode node) + { + final int len = node.getChildCount(); + if (!makingSimpleArray) + { + for (int i = 0; i < len; i++) + { + getMXMLWalker().walk(node.getChild(i)); // props in object + } + } + else + { + MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps"); + if (ps.value == null) + ps.value = ""; + ps.value += "{"; + for (int i = 0; i < len; i++) + { + IMXMLPropertySpecifierNode propName = (IMXMLPropertySpecifierNode)node.getChild(i); + ps.value += propName.getName() + ": "; + getMXMLWalker().walk(propName.getChild(0)); + if (i < len - 1) + ps.value += ", "; + } + ps.value += "}"; + } + } + + @Override + public void emitArray(IMXMLArrayNode node) + { + moveDown(false, null, null); + + boolean isSimple = true; + final int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + final IASNode child = node.getChild(i); + ASTNodeID nodeID = child.getNodeID(); + if (nodeID == ASTNodeID.MXMLArrayID || nodeID == ASTNodeID.MXMLInstanceID || nodeID == ASTNodeID.MXMLStateID) + { + isSimple = false; + break; + } + } + boolean oldMakingSimpleArray = makingSimpleArray; + MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps"); + if (isSimple) + { + makingSimpleArray = true; + ps.value = ASEmitterTokens.SQUARE_OPEN.getToken(); + } + for (int i = 0; i < len; i++) + { + getMXMLWalker().walk(node.getChild(i)); // Instance + if (isSimple && i < len - 1) + ps.value += ASEmitterTokens.COMMA.getToken(); + } + if (isSimple) + { + ps.value += ASEmitterTokens.SQUARE_CLOSE.getToken(); + } + makingSimpleArray = oldMakingSimpleArray; + + moveUp(false, false); + } + + @Override + public void emitString(IMXMLStringNode node) + { + getCurrentDescriptor("ps").valueNeedsQuotes = true; + + emitAttributeValue(node); + } + + //-------------------------------------------------------------------------- + + @Override + public void emitLiteral(IMXMLLiteralNode node) + { + MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps"); + if (ps.value == null) // might be non-null if makingSimpleArray + ps.value = ""; + + if (ps.valueNeedsQuotes) + ps.value += ASEmitterTokens.SINGLE_QUOTE.getToken(); + + String s = node.getValue().toString(); + if (ps.valueNeedsQuotes) + { + // escape all single quotes found within the string + s = s.replace(ASEmitterTokens.SINGLE_QUOTE.getToken(), + "\\" + ASEmitterTokens.SINGLE_QUOTE.getToken()); + } + ps.value += s; + + if (ps.valueNeedsQuotes) + ps.value += ASEmitterTokens.SINGLE_QUOTE.getToken(); + } + + //-------------------------------------------------------------------------- + + @Override + public void emitFactory(IMXMLFactoryNode node) + { + MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps"); + ps.value = "new " + formatQualifiedName("org.apache.flex.core.ClassFactory") + "("; + + IASNode cnode = node.getChild(0); + if (cnode instanceof IMXMLClassNode) + { + ps.value += formatQualifiedName(((IMXMLClassNode)cnode).getValue(getMXMLWalker().getProject()).getQualifiedName()); + } + ps.value += ")"; + } + + //-------------------------------------------------------------------------- + + @Override + public void emitComponent(IMXMLComponentNode node) + { + MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps"); + ps.value = "new " + formatQualifiedName("org.apache.flex.core.ClassFactory") + "("; + + ps.value += formatQualifiedName(documentDefinition.getQualifiedName()) + "."; + ps.value += formatQualifiedName(node.getName()); + ps.value += ")"; + + setBufferWrite(true); + emitSubDocument(node); + subDocuments.append(getBuilder().toString()); + getBuilder().setLength(0); + setBufferWrite(false); + } + + @Override + protected void setBufferWrite(boolean value) + { + super.setBufferWrite(value); + IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()).getASEmitter(); + ((JSFlexJSEmitter)asEmitter).setBufferWrite(value); + } + + //-------------------------------------------------------------------------- + // JS output + //-------------------------------------------------------------------------- + + private void emitHeader(IMXMLDocumentNode node) + { + String cname = node.getFileNode().getName(); + String bcname = node.getBaseClassName(); + + FlexJSProject project = (FlexJSProject) getMXMLWalker().getProject(); + List<File> sourcePaths = project.getSourcePath(); + String sourceName = node.getSourcePath(); + for (File sourcePath : sourcePaths) + { + if (sourceName.startsWith(sourcePath.getAbsolutePath())) + { + sourceName = sourceName.substring(sourcePath.getAbsolutePath().length() + 1); + } + } + writeNewline("/**"); + writeNewline(" * Generated by Apache Flex Cross-Compiler from " + sourceName); + writeNewline(" * " + cname); + writeNewline(" *"); + writeNewline(" * @fileoverview"); + writeNewline(" *"); + writeNewline(" * @suppress {checkTypes|accessControls}"); + writeNewline(" */"); + writeNewline(); + + ArrayList<String> writtenInstances = new ArrayList<String>(); + emitHeaderLine(cname, true); // provide + for (String subDocumentName : subDocumentNames) + { + emitHeaderLine(subDocumentName, true); + writtenInstances.add(formatQualifiedName(subDocumentName)); + } + writeNewline(); + emitHeaderLine(bcname); + writtenInstances.add(formatQualifiedName(cname)); // make sure we don't add ourselves + writtenInstances.add(formatQualifiedName(bcname)); // make sure we don't add the baseclass twice + allInstances.addAll(0, instances); + for (MXMLDescriptorSpecifier instance : allInstances) + { + String name = instance.name; + if (writtenInstances.indexOf(name) == -1) + { + emitHeaderLine(name); + writtenInstances.add(name); + } + } + ASProjectScope projectScope = (ASProjectScope) project.getScope(); + IDefinition cdef = node.getDefinition(); + ICompilationUnit cu = projectScope + .getCompilationUnitForDefinition(cdef); + ArrayList<String> deps = project.getRequires(cu); + + if (interfaceList != null) + { + String[] interfaces = interfaceList.split(", "); + for (String iface : interfaces) + { + deps.add(iface); + usedNames.add(iface); + } + } + if (deps != null) + { + Collections.sort(deps); + for (String imp : deps) + { + if (imp.indexOf(JSGoogEmitterTokens.AS3.getToken()) != -1) + continue; + + if (imp.equals(cname)) + continue; + + if (imp.equals("mx.binding.Binding")) + continue; + if (imp.equals("mx.binding.BindingManager")) + continue; + if (imp.equals("mx.binding.FunctionReturnWatcher")) + continue; + if (imp.equals("mx.binding.PropertyWatcher")) + continue; + if (imp.equals("mx.binding.StaticPropertyWatcher")) + continue; + if (imp.equals("mx.binding.XMLWatcher")) + continue; + if (imp.equals("mx.events.PropertyChangeEvent")) + continue; + if (imp.equals("mx.events.PropertyChangeEventKind")) + continue; + if (imp.equals("mx.core.DeferredInstanceFromFunction")) + continue; + + if (NativeUtils.isNative(imp)) + continue; + + String formatted = formatQualifiedName(imp, false); + if (writtenInstances.indexOf(formatted) == -1) + { + emitHeaderLine(imp); + writtenInstances.add(formatted); + } + } + } + + // erikdebruin: Add missing language feature support, like the 'is' and + // 'as' operators. We don't need to worry about requiring + // this in every project: ADVANCED_OPTIMISATIONS will NOT + // include any of the code if it is not used in the project. + if (project.mainCU != null && + cu.getName().equals(project.mainCU.getName())) + { + if (project instanceof FlexJSProject) + { + if (((FlexJSProject)project).needLanguage) + emitHeaderLine(JSFlexJSEmitterTokens.LANGUAGE_QNAME.getToken()); + } + } + + writeNewline(); + writeNewline(); + } + + private void emitHeaderLine(String qname) + { + emitHeaderLine(qname, false); + } + + private void emitHeaderLine(String qname, boolean isProvide) + { + write((isProvide) ? JSGoogEmitterTokens.GOOG_PROVIDE + : JSGoogEmitterTokens.GOOG_REQUIRE); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write(formatQualifiedName(qname, false)); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + writeNewline(ASEmitterTokens.SEMICOLON); + } + + //-------------------------------------------------------------------------- + // Utils + //-------------------------------------------------------------------------- + + @Override + protected void emitAttributeValue(IASNode node) + { + IMXMLLiteralNode cnode = (IMXMLLiteralNode) node.getChild(0); + + if (cnode.getValue() != null) + getMXMLWalker().walk((IASNode) cnode); // Literal + } + + private MXMLDescriptorSpecifier getCurrentDescriptor(String type) + { + MXMLDescriptorSpecifier currentDescriptor = null; + + int index; + + if (type.equals("i")) + { + index = currentInstances.size() - 1; + if (index > -1) + currentDescriptor = currentInstances.get(index); + } + else if (type.equals("so")) + { + return currentStateOverrides; + } + else + { + index = currentPropertySpecifiers.size() - 1; + if (index > -1) + currentDescriptor = currentPropertySpecifiers.get(index); + } + + return currentDescriptor; + } + + protected void moveDown(boolean byPass, + MXMLDescriptorSpecifier currentInstance, + MXMLDescriptorSpecifier currentPropertySpecifier) + { + if (!byPass) + { + if (currentInstance != null) + currentInstances.add(currentInstance); + } + + if (currentPropertySpecifier != null) + currentPropertySpecifiers.add(currentPropertySpecifier); + } + + protected void moveUp(boolean byPass, boolean isInstance) + { + if (!byPass) + { + int index; + + if (isInstance) + { + index = currentInstances.size() - 1; +
<TRUNCATED>