blob: ce5b260883331b23f2e00cc0c0af60bac93e6316 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.royale.compiler.internal.codegen.mxml.royale;
import java.io.File;
import java.io.FilterWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
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 java.util.Stack;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.instructionlist.InstructionList;
import org.apache.royale.abc.semantics.Instruction;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.OneOperandInstruction;
import org.apache.royale.compiler.codegen.ISubEmitter;
import org.apache.royale.compiler.codegen.as.IASEmitter;
import org.apache.royale.compiler.codegen.js.IJSEmitter;
import org.apache.royale.compiler.codegen.mxml.js.IMXMLJSEmitter;
import org.apache.royale.compiler.codegen.mxml.royale.IMXMLRoyaleEmitter;
import org.apache.royale.compiler.common.ASModifier;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.constants.IASKeywordConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.internal.as.codegen.InstructionListNode;
import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
import org.apache.royale.compiler.internal.codegen.databinding.BindingDatabase;
import org.apache.royale.compiler.internal.codegen.databinding.BindingInfo;
import org.apache.royale.compiler.internal.codegen.databinding.FunctionWatcherInfo;
import org.apache.royale.compiler.internal.codegen.databinding.PropertyWatcherInfo;
import org.apache.royale.compiler.internal.codegen.databinding.StaticPropertyWatcherInfo;
import org.apache.royale.compiler.internal.codegen.databinding.WatcherInfoBase;
import org.apache.royale.compiler.internal.codegen.databinding.WatcherInfoBase.WatcherType;
import org.apache.royale.compiler.internal.codegen.databinding.XMLWatcherInfo;
import org.apache.royale.compiler.internal.codegen.js.JSEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.JSSessionModel.PropertyNodes;
import org.apache.royale.compiler.internal.codegen.js.JSSessionModel.BindableVarInfo;
import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter;
import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleBasicMXMLDescriptorEmitter;
import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogDocEmitter;
import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.jx.BindableEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.PackageFooterEmitter;
import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
import org.apache.royale.compiler.internal.codegen.mxml.MXMLEmitter;
import org.apache.royale.compiler.internal.driver.js.royale.JSCSSCompilationSession;
import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.scopes.ASProjectScope;
import org.apache.royale.compiler.internal.targets.ITargetAttributes;
import org.apache.royale.compiler.internal.tree.as.FunctionCallNode;
import org.apache.royale.compiler.internal.tree.as.IdentifierNode;
import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode;
import org.apache.royale.compiler.internal.tree.as.VariableNode;
import org.apache.royale.compiler.internal.tree.mxml.MXMLDocumentNode;
import org.apache.royale.compiler.internal.tree.mxml.MXMLFileNode;
import org.apache.royale.compiler.internal.tree.mxml.MXMLFunctionNode;
import org.apache.royale.compiler.internal.tree.mxml.MXMLBindingNode;
import org.apache.royale.compiler.mxml.IMXMLLanguageConstants;
import org.apache.royale.compiler.problems.FileNotFoundProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.projects.IRoyaleProject;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.*;
import org.apache.royale.compiler.tree.metadata.IMetaTagNode;
import org.apache.royale.compiler.tree.metadata.IMetaTagsNode;
import org.apache.royale.compiler.tree.mxml.*;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.utils.NativeUtils;
import org.apache.royale.compiler.visitor.mxml.IMXMLBlockWalker;
import org.apache.royale.swc.ISWC;
import com.google.common.base.Joiner;
import com.google.common.io.Files;
import com.google.debugging.sourcemap.FilePosition;
/**
* @author Erik de Bruin
*/
public class MXMLRoyaleEmitter extends MXMLEmitter implements
IMXMLRoyaleEmitter, IMXMLJSEmitter
{
// 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<IMXMLScriptNode> scripts;
//private ArrayList<MXMLStyleSpecifier> styles;
private IClassDefinition classDefinition;
private IClassDefinition documentDefinition;
private ArrayList<String> usedNames = new ArrayList<String>();
private ArrayList<String> staticUsedNames = new ArrayList<String>();
private ArrayList<IMXMLMetadataNode> metadataNodes = new ArrayList<IMXMLMetadataNode>();
// separately track all fx:Declarations that are primitive types (fx:String, fx:Array)
private ArrayList<IMXMLInstanceNode> primitiveDeclarationNodes = new ArrayList<IMXMLInstanceNode>();
private int eventCounter;
private int idCounter;
private int bindingCounter;
private boolean inMXMLContent;
private IMXMLInstanceNode overrideInstanceToEmit;
private Stack<IMXMLStateNode> inStatesOverride = new Stack<IMXMLStateNode>();
private boolean makingSimpleArray;
private boolean inStaticInitializer;
private StringBuilder subDocuments = new StringBuilder();
private ArrayList<String> subDocumentNames = new ArrayList<String>();
private String interfaceList;
private boolean emitExports = true;
private ISubEmitter<MXMLDescriptorSpecifier> mxmlDescriptorEmitter;
/**
* 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;
private SourceMapMapping lastMapping;
private List<SourceMapMapping> sourceMapMappings;
public List<SourceMapMapping> getSourceMapMappings()
{
return sourceMapMappings;
}
public MXMLRoyaleEmitter(FilterWriter out)
{
super(out);
sourceMapMappings = new ArrayList<SourceMapMapping>();
mxmlDescriptorEmitter = new JSRoyaleBasicMXMLDescriptorEmitter(this);
}
@Override
public String postProcess(String output)
{
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()).getASEmitter();
ArrayList<String> asEmitterUsedNames = ((JSRoyaleEmitter)asEmitter).usedNames;
JSRoyaleEmitter fjs = (JSRoyaleEmitter)asEmitter;
String currentClassName = fjs.getModel().getCurrentClass().getQualifiedName();
ArrayList<String> removals = new ArrayList<String>();
for (String usedName : asEmitterUsedNames) {
//remove any internal component that has been registered with the other emitter's usedNames
if (usedName.startsWith(currentClassName+".") && subDocumentNames.contains(usedName.substring(currentClassName.length()+1))) {
removals.add(usedName);
}
}
for (String usedName : removals)
{
asEmitterUsedNames.remove(usedName);
}
RoyaleJSProject fjp = (RoyaleJSProject) getMXMLWalker().getProject();
if (fjp.config == null || fjp.config.isVerbose())
{
System.out.println(currentClassName + " as: " + asEmitterUsedNames.toString());
System.out.println(currentClassName + " mxml: " + usedNames.toString());
}
usedNames.addAll(asEmitterUsedNames);
ArrayList<String> asStaticEmitterUsedNames = ((JSRoyaleEmitter)asEmitter).staticUsedNames;
removals = new ArrayList<String>();
for (String usedName : asStaticEmitterUsedNames) {
//remove any internal component that has been registered with the other emitter's usedNames
if (usedName.startsWith(currentClassName+".") && subDocumentNames.contains(usedName.substring(currentClassName.length()+1))) {
removals.add(usedName);
}
}
for (String usedName : removals)
{
asStaticEmitterUsedNames.remove(usedName);
}
if (fjp.config == null || fjp.config.isVerbose())
{
System.out.println(currentClassName + " as: " + asStaticEmitterUsedNames.toString());
System.out.println(currentClassName + " mxml: " + staticUsedNames.toString());
}
staticUsedNames.addAll(asStaticEmitterUsedNames);
boolean foundXML = false;
String[] lines = output.split("\n");
ArrayList<String> finalLines = new ArrayList<String>();
int endRequires = -1;
boolean sawRequires = false;
boolean stillSearching = true;
int provideIndex = -1;
ArrayList<String> namesToAdd = new ArrayList<String>();
ArrayList<String> foundRequires = new ArrayList<String>();
int len = lines.length;
for (int i = 0; i < len; i++)
{
String line = lines[i];
if (stillSearching)
{
if (provideIndex == -1 || !sawRequires)
{
int c = line.indexOf(JSGoogEmitterTokens.GOOG_PROVIDE.getToken());
if (c != -1)
{
// if zero requires are found, require Language after the
// call to goog.provide
provideIndex = i + 1;
}
}
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;
foundRequires.add(s);
if (!usedNames.contains(s))
{
removeLineFromMappings(i);
continue;
}
}
else if (sawRequires)
{
// append info() structure if main CU
ICompilerProject project = getMXMLWalker().getProject();
RoyaleJSProject royaleProject = null;
if (project instanceof RoyaleJSProject)
royaleProject = (RoyaleJSProject) project;
stillSearching = false;
for (String usedName :usedNames) {
if (!foundRequires.contains(usedName)) {
if (usedName.equals(classDefinition.getQualifiedName())) continue;
if (((JSRoyaleEmitter) asEmitter).getModel().isInternalClass(usedName)) continue;
if (subDocumentNames.contains(usedName)) continue;
if (royaleProject != null)
{
if (!isGoogProvided(usedName))
{
continue;
}
ICompilationUnit cu = royaleProject.resolveQNameToCompilationUnit(usedName);
if (cu == null)
{
System.out.println("didn't find CompilationUnit for " + usedName);
}
}
namesToAdd.add(usedName);
}
}
for (String nameToAdd : namesToAdd) {
finalLines.add(createRequireLine(nameToAdd,false));
addLineToMappings(i);
}
endRequires = finalLines.size();
}
}
finalLines.add(line);
}
boolean needXML = ((RoyaleJSProject)(((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());
addLineToMappings(endRequires);
endRequires++;
}
// append info() structure if main CU
ICompilerProject project = getMXMLWalker().getProject();
if (project instanceof RoyaleJSProject)
{
RoyaleJSProject royaleProject = (RoyaleJSProject) project;
if (royaleProject.mainCU != null)
{
String mainDef = null;
try {
mainDef = royaleProject.mainCU.getQualifiedNames().get(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String thisDef = documentDefinition.getQualifiedName();
if (mainDef != null && mainDef.equals(thisDef))
{
String infoInject = "\n\n" + thisDef + ".prototype.info = function() {\n" +
" return { ";
String sep = "";
Set<String> mixins = royaleProject.mixinClassNames;
if (mixins.size() > 0)
{
String mixinInject = "\"mixins\": [";
boolean firstOne = true;
for (String mixin : mixins)
{
if (!isGoogProvided(mixin))
{
continue;
}
if (!firstOne)
{
mixinInject += ", ";
}
mixinInject += 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(endRequires);
endRequires++;
}
mixinInject += "]";
infoInject += mixinInject;
sep = ",\n";
}
Map<String, String> aliases = royaleProject.remoteClassAliasMap;
if (aliases != null && aliases.size() > 0)
{
String aliasInject = sep + "\"remoteClassAliases\": {";
boolean firstOne = true;
for (String className : aliases.keySet())
{
if (!isGoogProvided(className))
{
continue;
}
if (!firstOne)
{
aliasInject += ", ";
}
aliasInject += "\"" + className + "\": ";
String alias = aliases.get(className);
aliasInject += "\"" + alias + "\"";
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(className);
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
finalLines.add(endRequires, appendString.toString());
addLineToMappings(endRequires);
endRequires++;
}
aliasInject += "}";
infoInject += aliasInject;
sep = ",\n";
}
Collection<String> locales = royaleProject.getLocales();
if (locales.size() > 0)
{
String localeInject = sep + "\"compiledLocales\": [";
boolean firstOne = true;
String[] localeNames = new String[locales.size()];
locales.toArray(localeNames);
for (String locale : localeNames)
{
if (!firstOne)
{
localeInject += ", ";
}
localeInject += "\"" + locale + "\"";
firstOne = false;
}
localeInject += "]";
infoInject += localeInject;
sep = ",\n";
}
List<String> bundles = royaleProject.compiledResourceBundleNames;
if (bundles.size() > 0)
{
String bundleInject = sep + "\"compiledResourceBundleNames\": [";
boolean firstOne = true;
for (String bundle : bundles)
{
if (!firstOne)
{
bundleInject += ", ";
}
bundleInject += "\"" + bundle + "\"";
firstOne = false;
}
bundleInject += "]";
infoInject += bundleInject;
sep = ",\n";
}
List<String> bundleClasses = royaleProject.compiledResourceBundleClasses;
if (bundles.size() > 0)
{
for (String bundleClass : bundleClasses)
{
StringBuilder appendString = new StringBuilder();
appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
appendString.append(ASEmitterTokens.PAREN_OPEN.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(bundleClass);
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
finalLines.add(endRequires, appendString.toString());
addLineToMappings(endRequires);
endRequires++;
}
}
boolean isMX = false;
List<ISWC> swcs = royaleProject.getLibraries();
for (ISWC swc : swcs)
{
if (swc.getSWCFile().getName().equalsIgnoreCase("MX.swc"))
{
isMX = true;
break;
}
}
if (isMX)
{
MXMLDocumentNode mxmlDoc = (MXMLDocumentNode)documentDefinition.getNode();
if (mxmlDoc != null)
{
MXMLFileNode mxmlFile = (MXMLFileNode)mxmlDoc.getParent();
if (mxmlFile != null)
{
ITargetAttributes attrs = mxmlFile.getTargetAttributes(royaleProject);
if (attrs != null && attrs.getUsePreloader() != null)
{
String preloaderInject = sep + IMXMLLanguageConstants.ATTRIBUTE_USE_PRELOADER + ": ";
preloaderInject += attrs.getUsePreloader() == Boolean.TRUE ? "true" : "false";
sep = ",\n";
infoInject += preloaderInject;
}
}
}
}
String contextRoot = royaleProject.getServciesContextRoot();
if (contextRoot != null)
{
String contextInject = sep + "\"contextRoot\"" + ": ";
contextInject += "'" + contextRoot.trim() + "'";
sep = ",\n";
infoInject += contextInject;
}
String servicesPath = royaleProject.getServicesXMLPath();
if (servicesPath != null)
{
File servicesFile = new File(servicesPath);
if (!servicesFile.exists())
{
FileNotFoundProblem prob = new FileNotFoundProblem(servicesPath);
royaleProject.getProblems().add(prob);
}
else
{
// should use XML parser to skip over comments
// but this will work for now
List<String> fileLines = null;
try {
fileLines = Files.readLines(new File(servicesPath), Charset.forName("utf8"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
StringBuffer sb = new StringBuffer();
boolean inComment = false;
boolean inChannels = false;
for (String s : fileLines)
{
s = s.trim();
if (s.contains("<!--"))
{
if (!s.contains("-->"))
inComment = true;
continue;
}
if (inComment)
{
if (s.contains("-->"))
inComment = false;
continue;
}
if (s.contains("service-include"))
{
int c = s.indexOf("file-path");
c = s.indexOf("\"", c);
int c2 = s.indexOf("\"", c + 1);
String filePath = s.substring(c + 1, c2);
File subFile = new File(servicesFile.getParentFile(), filePath);
List<String> subfileLines;
try {
subfileLines = Files.readLines(subFile, Charset.forName("utf8"));
s = getSubFileContent(subfileLines);
sb.append(s);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
sb.append(s + " ");
if (s.contains("<channel-definition"))
inChannels = true;
if (s.contains("<endpoint"))
inChannels = false;
if (inChannels && s.contains("class"))
{
int c = s.indexOf("class");
c = s.indexOf("\"", c);
int c2 = s.indexOf("\"", c + 1);
String className = s.substring(c + 1, c2);
StringBuilder appendString = new StringBuilder();
appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
appendString.append(ASEmitterTokens.PAREN_OPEN.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(className);
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
finalLines.add(endRequires, appendString.toString());
addLineToMappings(endRequires);
endRequires++;
}
}
}
String servicesInject = sep + "\"servicesConfig\"" + ": ";
servicesInject += "'" + sb.toString().trim() + "'";
sep = ",\n";
infoInject += servicesInject;
}
}
infoInject += "}};";
finalLines.add(infoInject);
int newLineIndex = 0;
while((newLineIndex = infoInject.indexOf('\n', newLineIndex)) != -1)
{
addLineToMappings(finalLines.size());
newLineIndex++;
}
String cssInject = "\n\n" + thisDef + ".prototype.cssData = [";
JSCSSCompilationSession cssSession = (JSCSSCompilationSession) royaleProject.getCSSCompilationSession();
String s = cssSession.getEncodedCSS();
if (s != null)
{
int reqidx = s.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
if (reqidx != -1)
{
String cssRequires = s.substring(reqidx);
s = s.substring(0, reqidx - 1);
String[] cssRequireLines = cssRequires.split("\n");
for(String require : cssRequireLines)
{
finalLines.add(endRequires, require);
addLineToMappings(endRequires);
endRequires++;
}
}
cssInject += s;
finalLines.add(cssInject);
newLineIndex = 0;
while((newLineIndex = cssInject.indexOf('\n', newLineIndex)) != -1)
{
addLineToMappings(finalLines.size());
newLineIndex++;
}
}
}
}
}
if (staticUsedNames.size() > 0)
{
if (staticUsedNames.size() > 1 ||
!staticUsedNames.get(0).equals(currentClassName))
{
StringBuilder sb = new StringBuilder();
sb.append(JSGoogEmitterTokens.ROYALE_STATIC_DEPENDENCY_LIST.getToken());
boolean firstDependency = true;
for (String staticName : staticUsedNames)
{
if (currentClassName.equals(staticName))
continue;
if (!firstDependency)
sb.append(",");
firstDependency = false;
sb.append(staticName);
}
sb.append("*/");
finalLines.add(provideIndex, sb.toString());
addLineToMappings(provideIndex);
}
}
return Joiner.on("\n").join(finalLines);
}
private String getSubFileContent(List<String> subfileLines) {
StringBuffer sb = new StringBuffer();
for (String s : subfileLines)
{
s = s.trim();
if (s.startsWith("<?xml"))
continue;
else
{
sb.append(s + " ");
}
}
return sb.toString();
}
public void startMapping(ISourceLocation node)
{
startMapping(node, node.getLine(), node.getColumn());
}
public void startMapping(ISourceLocation node, int line, int column)
{
if (isBufferWrite())
{
return;
}
if (lastMapping != null)
{
FilePosition sourceStartPosition = lastMapping.sourceStartPosition;
throw new IllegalStateException("Cannot start new mapping when another mapping is already started. "
+ "Previous mapping at Line " + sourceStartPosition.getLine()
+ " and Column " + sourceStartPosition.getColumn()
+ " in file " + lastMapping.sourcePath);
}
String sourcePath = node.getSourcePath();
if (sourcePath == null)
{
//if the source path is null, this node may have been generated by
//the compiler automatically. for example, an untyped variable will
//have a node for the * type.
if (node instanceof IASNode)
{
IASNode parentNode = ((IASNode) node).getParent();
if (parentNode != null)
{
//try the parent node
startMapping(parentNode, line, column);
return;
}
}
}
SourceMapMapping mapping = new SourceMapMapping();
mapping.sourcePath = sourcePath;
mapping.sourceStartPosition = new FilePosition(line, column);
mapping.destStartPosition = new FilePosition(getCurrentLine(), getCurrentColumn());
lastMapping = mapping;
}
public void startMapping(ISourceLocation node, ISourceLocation afterNode)
{
startMapping(node, afterNode.getEndLine(), afterNode.getEndColumn());
}
public void endMapping(ISourceLocation node)
{
if (isBufferWrite())
{
return;
}
if (lastMapping == null)
{
throw new IllegalStateException("Cannot end mapping when a mapping has not been started");
}
lastMapping.destEndPosition = new FilePosition(getCurrentLine(), getCurrentColumn());
sourceMapMappings.add(lastMapping);
lastMapping = null;
}
/**
* Adjusts the line numbers saved in the source map when a line should be
* added during post processing.
*
* @param lineIndex
*/
protected void addLineToMappings(int lineIndex)
{
for (SourceMapMapping mapping : sourceMapMappings)
{
FilePosition destStartPosition = mapping.destStartPosition;
int startLine = destStartPosition.getLine();
if(startLine > lineIndex)
{
mapping.destStartPosition = new FilePosition(startLine + 1, destStartPosition.getColumn());
FilePosition destEndPosition = mapping.destEndPosition;
mapping.destEndPosition = new FilePosition(destEndPosition.getLine() + 1, destEndPosition.getColumn());
}
}
}
/**
* Adjusts the line numbers saved in the source map when a line should be
* removed during post processing.
*
* @param lineIndex
*/
protected void removeLineFromMappings(int lineIndex)
{
for (SourceMapMapping mapping : sourceMapMappings)
{
FilePosition destStartPosition = mapping.destStartPosition;
int startLine = destStartPosition.getLine();
if(startLine > lineIndex)
{
mapping.destStartPosition = new FilePosition(startLine - 1, destStartPosition.getColumn());
FilePosition destEndPosition = mapping.destEndPosition;
mapping.destEndPosition = new FilePosition(destEndPosition.getLine() - 1, destEndPosition.getColumn());
}
}
}
@Override
protected String getIndent(int numIndent)
{
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < numIndent; i++)
sb.append(JSRoyaleEmitterTokens.INDENT.getToken());
return sb.toString();
}
//--------------------------------------------------------------------------
@Override
public void emitDeclarations(IMXMLDeclarationsNode node)
{
inMXMLContent = true;
boolean reusingDescriptor = false;
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i");
MXMLDescriptorSpecifier currentPropertySpecifier = new MXMLDescriptorSpecifier();
currentPropertySpecifier.isProperty = true;
currentPropertySpecifier.name = "mxmlContent";
currentPropertySpecifier.parent = currentInstance;
if (currentInstance == null)
{
ArrayList<MXMLDescriptorSpecifier> specList =
(currentInstance == null) ? descriptorTree : currentInstance.propertySpecifiers;
for (MXMLDescriptorSpecifier ds : specList)
{
if (ds.name.equals("mxmlContent"))
{
currentPropertySpecifier = ds;
reusingDescriptor = true;
break;
}
}
}
if (!reusingDescriptor)
descriptorTree.add(currentPropertySpecifier);
moveDown(false, currentInstance, currentPropertySpecifier);
super.emitDeclarations(node);
moveUp(false, false);
inMXMLContent = false;
}
@Override
public void emitDocument(IMXMLDocumentNode node)
{
RoyaleJSProject fjp = (RoyaleJSProject) getMXMLWalker().getProject();
if (fjp.config != null)
emitExports = fjp.config.getExportPublicSymbols();
descriptorTree = new ArrayList<MXMLDescriptorSpecifier>();
propertiesTree = new MXMLDescriptorSpecifier();
propertiesTree.name = node.getQualifiedName();
events = new ArrayList<MXMLEventSpecifier>();
instances = new ArrayList<MXMLDescriptorSpecifier>();
scripts = new ArrayList<IMXMLScriptNode>();
//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
JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
fjs.setBuilder(getBuilder());
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());
emitDeclarationVariables();
// emitMetaData(cdef);
write(subDocuments.toString());
writeNewline();
emitScripts();
fjs.getBindableEmitter().emit(cdef);
fjs.getAccessorEmitter().emit(cdef);
emitEvents(cname);
emitComplexStaticInitializers(node);
emitPropertyGetterSetters(cname);
emitMXMLDescriptorFuncs(cname);
emitBindingData(cname, cdef);
emitMetaData(cdef);
emitSourceMapDirective(node);
}
public void emitDeclarationVariables()
{
for (IMXMLInstanceNode node : primitiveDeclarationNodes)
{
String id = node.getEffectiveID();
writeNewline();
writeNewline("/**");
writeNewline(" * @export");
writeNewline(" * @type {" + JSGoogDocEmitter.convertASTypeToJSType(formatQualifiedName(node.getName()), "") + "}");
writeNewline(" */");
String cname = node.getFileNode().getName();
write(cname);
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSEmitterTokens.PROTOTYPE);
write(ASEmitterTokens.MEMBER_ACCESS);
write(id);
writeNewline(ASEmitterTokens.SEMICOLON);
}
}
public void emitSubDocument(IMXMLComponentNode node)
{
ArrayList<MXMLDescriptorSpecifier> oldDescriptorTree;
MXMLDescriptorSpecifier oldPropertiesTree;
MXMLDescriptorSpecifier oldStateOverrides;
ArrayList<MXMLEventSpecifier> oldEvents;
ArrayList<IMXMLScriptNode> oldScripts;
ArrayList<MXMLDescriptorSpecifier> oldCurrentInstances;
ArrayList<MXMLDescriptorSpecifier> oldInstances;
ArrayList<MXMLDescriptorSpecifier> oldCurrentPropertySpecifiers;
int oldEventCounter;
int oldIdCounter;
boolean oldInMXMLContent;
String oldInterfaceList;
oldInterfaceList = interfaceList;
interfaceList = null;
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<IMXMLScriptNode>();
//styles = new ArrayList<MXMLStyleSpecifier>();
oldCurrentInstances = currentInstances;
currentInstances = new ArrayList<MXMLDescriptorSpecifier>();
oldCurrentPropertySpecifiers = currentPropertySpecifiers;
currentPropertySpecifiers = new ArrayList<MXMLDescriptorSpecifier>();
oldStateOverrides = currentStateOverrides;
currentStateOverrides = new 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();
((JSRoyaleEmitter) asEmitter).getModel().pushClass(cdef);
IASNode classNode = node.getContainedClassDefinitionNode();
String cname = cdef.getQualifiedName();
String baseClassName = cdef.getBaseClassAsDisplayString();
subDocumentNames.add(cname);
// visit tags
final int len = classNode.getChildCount();
for (int i = 0; i < len; i++)
{
getMXMLWalker().walk(classNode.getChild(i));
}
((JSRoyaleEmitter) asEmitter).mxmlEmitter = this;
emitClassDeclStart(cname, baseClassName, false);
emitComplexInitializers(classNode);
emitPropertyDecls();
emitClassDeclEnd(cname, baseClassName);
emitMetaData(cdef);
emitScripts();
emitEvents(cname);
emitPropertyGetterSetters(cname);
emitMXMLDescriptorFuncs(cname);
emitBindingData(cname, cdef);
write(((JSRoyaleEmitter) asEmitter).stringifyDefineProperties(cdef));
descriptorTree = oldDescriptorTree;
propertiesTree = oldPropertiesTree;
currentStateOverrides = oldStateOverrides;
events = oldEvents;
scripts = oldScripts;
currentInstances = oldCurrentInstances;
allInstances.addAll(instances);
instances = oldInstances;
currentPropertySpecifiers = oldCurrentPropertySpecifiers;
eventCounter = oldEventCounter;
idCounter = oldIdCounter;
inMXMLContent = oldInMXMLContent;
classDefinition = oldClassDef;
interfaceList = oldInterfaceList;
((JSRoyaleEmitter) asEmitter).getModel().popClass();
((JSRoyaleEmitter) asEmitter).mxmlEmitter = null;
}
@Override
public void emitMetadata(IMXMLMetadataNode node)
{
metadataNodes.add(node);
}
public void emitSourceMapDirective(ITypeNode node)
{
IMXMLBlockWalker walker = (IMXMLBlockWalker) getMXMLWalker();
IJSEmitter jsEmitter = (IJSEmitter) walker.getASEmitter();
jsEmitter.emitSourceMapDirective(node);
}
//--------------------------------------------------------------------------
protected void emitClassDeclStart(String cname, String baseClassName,
boolean indent)
{
writeNewline();
writeNewline("/**");
writeNewline(" * @constructor");
writeNewline(" * @extends {" + formatQualifiedName(baseClassName) + "}");
if (interfaceList != null && interfaceList.length() > 0)
{
String[] interfaces = interfaceList.split(",");
for (String iface : interfaces)
{
writeNewline(" * @implements {" + formatQualifiedName(iface.trim()) + "}");
}
}
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;
collectExportedNames(root);
writeNewline("/**");
if (emitExports)
writeNewline(" * @export");
writeNewline(" * @type {Array}");
writeNewline(" */");
writeNewline("this.mxmlsd = " + ASEmitterTokens.SQUARE_OPEN.getToken());
indentPush();
for (MXMLDescriptorSpecifier md : root.propertySpecifiers)
{
write(ASEmitterTokens.SQUARE_OPEN);
mxmlDescriptorEmitter.emit(md);
write(ASEmitterTokens.SQUARE_CLOSE);
writeNewline(ASEmitterTokens.COMMA);
}
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();
write("this.generateMXMLAttributes");
write(ASEmitterTokens.PAREN_OPEN);
indentPush();
writeNewline(ASEmitterTokens.SQUARE_OPEN);
MXMLDescriptorSpecifier root = propertiesTree;
root.isTopNode = true;
mxmlDescriptorEmitter.emit(root);
indentPop();
writeNewline();
collectExportedNames(root);
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();
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.ROYALE_CLASS_INFO = { names: [{ name: '");
write(cdef.getBaseName());
write("', qName: '");
write(formatQualifiedName(cname));
write("'");
writeToken(ASEmitterTokens.COMMA);
write(JSRoyaleEmitterTokens.ROYALE_CLASS_INFO_KIND);
writeToken(ASEmitterTokens.COLON);
write(ASEmitterTokens.SINGLE_QUOTE);
write(JSRoyaleEmitterTokens.ROYALE_CLASS_INFO_CLASS_KIND);
writeToken(ASEmitterTokens.SINGLE_QUOTE);
write(" }]");
if (interfaceList != null)
{
write(", interfaces: [");
write(interfaceList);
write("]");
}
write(" };");
emitReflectionData(cdef);
writeNewline();
writeNewline();
}
private void emitReflectionData(IClassDefinition cdef)
{
JSRoyaleEmitter asEmitter = (JSRoyaleEmitter)((IMXMLBlockWalker) getMXMLWalker()).getASEmitter();
RoyaleJSProject fjs = (RoyaleJSProject) getMXMLWalker().getProject();
ArrayList<String> exportProperties = new ArrayList<String>();
ArrayList<String> exportSymbols = new ArrayList<String>();
Set<String> exportMetadata = Collections.<String> emptySet();
if (fjs.config != null)
exportMetadata = fjs.config.getCompilerKeepCodeWithMetadata();
ArrayList<PackageFooterEmitter.VariableData> varData = new ArrayList<PackageFooterEmitter.VariableData>();
// vars can only come from script blocks and decls?
for (IMXMLInstanceNode declNode : primitiveDeclarationNodes)
{
PackageFooterEmitter.VariableData data = asEmitter.packageFooterEmitter.new VariableData();
varData.add(data);
data.name = declNode.getEffectiveID();
data.isStatic = false;
String qualifiedTypeName = declNode.getName();
data.type = (qualifiedTypeName);
}
List<IVariableNode> vars = asEmitter.getModel().getVars();
for (IVariableNode varNode : vars)
{
String ns = varNode.getNamespace();
if (ns == IASKeywordConstants.PUBLIC && !varNode.isConst())
{
PackageFooterEmitter.VariableData data = asEmitter.packageFooterEmitter.new VariableData();
varData.add(data);
data.name = varNode.getName();
data.isStatic = varNode.hasModifier(ASModifier.STATIC);
String qualifiedTypeName = "*";
IExpressionNode variableTypeNode = varNode.getVariableTypeNode();
if (variableTypeNode != null)
{
qualifiedTypeName = variableTypeNode.resolveType(getMXMLWalker().getProject()).getQualifiedName();
}
data.type = qualifiedTypeName;
IMetaTagsNode metaData = varNode.getMetaTags();
if (metaData != null)
{
IMetaTagNode[] tags = metaData.getAllTags();
if (tags.length > 0)
{
data.metaData = tags;
for (IMetaTagNode tag : tags)
{
String tagName = tag.getTagName();
if (exportMetadata.contains(tagName))
{
if (data.isStatic)
exportSymbols.add(data.name);
else
exportProperties.add(data.name);
}
}
}
}
}
}
ArrayList<PackageFooterEmitter.AccessorData> accessorData = new ArrayList<PackageFooterEmitter.AccessorData>();
HashMap<String, PropertyNodes> accessors = asEmitter.getModel().getPropertyMap();
//instance accessors
collectAccessors(accessors,accessorData,cdef);
accessors = asEmitter.getModel().getStaticPropertyMap();
//static accessors
collectAccessors(accessors,accessorData,cdef);
//additional bindables
HashMap<String, BindableVarInfo> bindableVars = asEmitter.getModel().getBindableVars();
for (String varName : bindableVars.keySet())
{
BindableVarInfo bindableVarInfo = bindableVars.get(varName);
String ns = bindableVarInfo.namespace;
if (ns == IASKeywordConstants.PUBLIC)
{
PackageFooterEmitter.AccessorData data = asEmitter.packageFooterEmitter.new AccessorData();
accessorData.add(data);
data.name = varName;
data.isStatic = bindableVarInfo.isStatic;
data.type = bindableVarInfo.type;
data.declaredBy = cdef.getQualifiedName();
data.access = "readwrite";
if (bindableVarInfo.metaTags != null) {
if (bindableVarInfo.metaTags.length > 0)
data.metaData = bindableVarInfo.metaTags;
}
}
}
for (MXMLDescriptorSpecifier instance : instances)
{
String instanceId = instance.id != null ? instance.id : (instance.hasLocalId ? instance.effectiveId : null);
if (instanceId != null)
{
PackageFooterEmitter.AccessorData data = asEmitter.packageFooterEmitter.new AccessorData();
accessorData.add(data);
data.name = instanceId;
data.type = instance.name;
data.access = "readwrite";
data.declaredBy = 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();
String qualifiedTypeName = methodNode.getReturnType();
if (!(qualifiedTypeName.equals("") || qualifiedTypeName.equals("void"))) {
IExpressionNode returnTypeNode = methodNode.getReturnTypeNode();
if (returnTypeNode != null)
{
qualifiedTypeName = returnTypeNode.resolveType(fjs).getQualifiedName();;
}
}
data.type = qualifiedTypeName;
data.declaredBy = cdef.getQualifiedName();
data.isStatic = methodNode.hasModifier(ASModifier.STATIC);
IParameterNode[] paramNodes = methodNode.getParameterNodes();
if (paramNodes != null && paramNodes.length > 0) {
data.parameters = paramNodes;
}
IMetaTagsNode metaData = methodNode.getMetaTags();
if (metaData != null)
{
IMetaTagNode[] tags = metaData.getAllTags();
if (tags.length > 0)
{
data.metaData = tags;
for (IMetaTagNode tag : tags)
{
String tagName = tag.getTagName();
if (exportMetadata.contains(tagName))
{
if (data.isStatic)
exportSymbols.add(data.name);
else
exportProperties.add(data.name);
}
}
}
}
}
}
if (cdef.getConstructor()==null) {
//add a constructor description for the reflection data
PackageFooterEmitter.MethodData data = asEmitter.packageFooterEmitter.new MethodData();
methodData.add(data);
data.name = cdef.getBaseName();
data.type = "";
data.isStatic = false;
data.declaredBy = cdef.getQualifiedName();
}
ArrayList<IMetaTagNode> metadataTagNodes = new ArrayList<IMetaTagNode>();
for (IMXMLMetadataNode metadataTag : metadataNodes)
{
IMetaTagNode[] tags = metadataTag.getMetaTagNodes();
//tags (MetaTagNodes) can be null if the parent node is empty (or content is commented out)
if (tags != null) {
for (IMetaTagNode tag : tags)
{
metadataTagNodes.add(tag);
}
}
}
IMetaTagNode[] metaDataTags = new IMetaTagNode[metadataTagNodes.size()];
asEmitter.packageFooterEmitter.emitReflectionData(
formatQualifiedName(cdef.getQualifiedName()),
PackageFooterEmitter.ReflectionKind.CLASS,
varData,
accessorData,
methodData,
metadataTagNodes.toArray(metaDataTags));
asEmitter.packageFooterEmitter.emitReflectionRegisterInitialStaticFields(
formatQualifiedName(cdef.getQualifiedName()),
cdef);
asEmitter.packageFooterEmitter.emitExportProperties(
formatQualifiedName(cdef.getQualifiedName()),
exportProperties,
exportSymbols);
}
private void collectAccessors(HashMap<String, PropertyNodes> accessors, ArrayList<PackageFooterEmitter.AccessorData> accessorData,IClassDefinition cdef ) {
JSRoyaleEmitter asEmitter = (JSRoyaleEmitter)((IMXMLBlockWalker) getMXMLWalker()).getASEmitter();
RoyaleJSProject fjs = (RoyaleJSProject) getMXMLWalker().getProject();
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.AccessorData data = asEmitter.packageFooterEmitter.new AccessorData();
accessorData.add(data);
data.name = accessorNode.getName();
data.isStatic = accessorNode.hasModifier(ASModifier.STATIC);
if (p.getter != null)
{
String returnType = "*";
IExpressionNode returnTypeNode = p.getter.getReturnTypeNode();
if (returnTypeNode != null)
{
returnType = returnTypeNode.resolveType(fjs).getQualifiedName();
}
data.type = returnType;
if (p.setter != null)
{
data.access = "readwrite";
}
else
{
data.access = "readonly";
}
}
else
{
String returnType = "*";
IExpressionNode variableTypeNode = p.setter.getVariableTypeNode();
if (variableTypeNode != null)
{
returnType = variableTypeNode.resolveType(fjs).getQualifiedName();
}
data.type = returnType;
data.access = "writeonly";
}
data.declaredBy = (cdef.getQualifiedName());
IMetaTagsNode metaData = accessorNode.getMetaTags();
if (metaData != null)
{
IMetaTagNode[] tags = metaData.getAllTags();
if (tags.length > 0)
{
data.metaData = tags;
/* accessors don't need exportProp since they are referenced via the defineProp data structure
for (IMetaTagNode tag : tags)
{
String tagName = tag.getTagName();
if (exportMetadata.contains(tagName))
{
if (data.isStatic)
exportSymbols.add(data.name);
else
exportProperties.add(data.name);
}
}
*/
}
}
}
}
}
//--------------------------------------------------------------------------
protected void emitPropertyDecls()
{
for (MXMLDescriptorSpecifier instance : instances)
{
String id = instance.id != null ? instance.id : instance.effectiveId;
if (id != null) { //it seems id can be null, for example with a generated Object for Operations via RemoteObject
writeNewline();
writeNewline("/**");
writeNewline(" * @private");
writeNewline(" * @type {" + instance.name + "}");
writeNewline(" */");
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
if (!id.startsWith(MXMLRoyaleEmitterTokens.ID_PREFIX.getToken())) id += "_";
write(id);
writeNewline(ASEmitterTokens.SEMICOLON);
}
}
}
//--------------------------------------------------------------------------
protected void emitBindingData(String cname, IClassDefinition cdef)
{
IRoyaleProject project = (IRoyaleProject)(walker.getProject());
BindingDatabase bd = project.getBindingMap().get(cdef);
if (bd == null)
return;
if (bd.getBindingInfo().isEmpty())
return;
inStaticInitializer = true;
outputBindingInfoAsData(cname, bd);
inStaticInitializer = false;
}
private void outputBindingInfoAsData(String cname, BindingDatabase bindingDataBase)
{
RoyaleJSProject project = (RoyaleJSProject)getMXMLWalker().getProject();
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
boolean allowDynamicBindings = project.config != null && project.config.getAllowDynamicBindings();
boolean useMxmlReflectObjectProperty = project.config != null && project.config.getMxmlReflectObjectProperty();
writeNewline("/**");
writeNewline(" * @export"); // must export or else GCC will remove it
writeNewline(" */");
writeNewline(formatQualifiedName(cname)
+ ".prototype._bindings = [");
if (bindingDataBase.getHasAncestorBindings()) {
//reference the ancestor binding data (which may in turn reference its owner's ancestor's bindings etc)
writeNewline(formatQualifiedName(bindingDataBase.getNearestAncestorWithBindings()) +
".prototype._bindings,");
}
Set<BindingInfo> bindingInfo = bindingDataBase.getBindingInfo();
writeNewline(bindingInfo.size() + ","); // number of bindings
boolean hadOutput = false;
for (BindingInfo bi : bindingInfo)
{
if (hadOutput)
{
writeNewline(ASEmitterTokens.COMMA);
}
hadOutput = true;
String s;
IMXMLNode node = bi.node;
if (node instanceof IMXMLSingleDataBindingNode)
{
IMXMLSingleDataBindingNode sbdn = (IMXMLSingleDataBindingNode)node;
IDefinition bdef = sbdn.getExpressionNode().resolve(project);
if (bdef != null)
{
//IDefinition cdef = bdef.getParent();
project.addExportedName(/*cdef.getQualifiedName() + "." + */bdef.getBaseName());
}
}
s = bi.getSourceString();
if (s == null && bi.isSourceSimplePublicProperty())
s = getSourceStringFromGetter(bi.getExpressionNodesForGetter());
if (!allowDynamicBindings || s == null || s.length() == 0)
{
List<IExpressionNode> getterNodes = bi.getExpressionNodesForGetter();
StringBuilder sb = new StringBuilder();
sb.append("/** @this {");
sb.append(formatQualifiedName(cname));
sb.append("} */\n");
sb.append("function() { return ");
int n = getterNodes.size();
for (int i = 0; i < n; i++)
{
IExpressionNode getterNode = getterNodes.get(i);
sb.append(asEmitter.stringifyNode(getterNode));
if (i < n - 1)
{
sb.append(ASEmitterTokens.SPACE.getToken());
sb.append(ASEmitterTokens.PLUS.getToken());
sb.append(ASEmitterTokens.SPACE.getToken());
}
}
sb.append("; },");
writeNewline(sb.toString());
}
else if (s.contains("."))
{
if (bi.classDef != null)
{
String[] parts = s.split("\\.");
String qname = bi.classDef.getQualifiedName();
write(ASEmitterTokens.SQUARE_OPEN);
write(ASEmitterTokens.DOUBLE_QUOTE);
write(qname);
write(ASEmitterTokens.DOUBLE_QUOTE);
if (!usedNames.contains(qname))
usedNames.add(qname);
if (!staticUsedNames.contains(qname))
staticUsedNames.add(qname);
StringBuilder objString = null;
if (useMxmlReflectObjectProperty)
{
objString = new StringBuilder();
objString.append(qname);
}
int n = parts.length;
for (int i = 1; i < n; i++)
{
String part = parts[i];
write(", ");
if(useMxmlReflectObjectProperty)
{
write(JSGoogEmitterTokens.GOOG_REFLECT_OBJECTPROPERTY);
write(ASEmitterTokens.PAREN_OPEN);
}
write(ASEmitterTokens.DOUBLE_QUOTE);
write(part);
write(ASEmitterTokens.DOUBLE_QUOTE);
if(useMxmlReflectObjectProperty)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(objString.toString());
write(ASEmitterTokens.PAREN_CLOSE);
objString.append(".");
objString.append(part);
}
}
write(ASEmitterTokens.SQUARE_CLOSE);
writeNewline(ASEmitterTokens.COMMA);
}
else
{
String[] parts = s.split("\\.");
write(ASEmitterTokens.SQUARE_OPEN);
StringBuilder objString = null;
if (useMxmlReflectObjectProperty)
{
objString = new StringBuilder();
objString.append("this");
}
int n = parts.length;
for (int i = 0; i < n; i++)
{
String part = parts[i];
if (i > 0)
{
write(", ");
}
if(useMxmlReflectObjectProperty)
{
write(JSGoogEmitterTokens.GOOG_REFLECT_OBJECTPROPERTY);
write(ASEmitterTokens.PAREN_OPEN);
}
write(ASEmitterTokens.DOUBLE_QUOTE);
write(part);
write(ASEmitterTokens.DOUBLE_QUOTE);
if(useMxmlReflectObjectProperty)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(objString.toString());
write(ASEmitterTokens.PAREN_CLOSE);
objString.append(".");
objString.append(part);
}
}
write(ASEmitterTokens.SQUARE_CLOSE);
writeNewline(ASEmitterTokens.COMMA);
}
}
else
{
if(useMxmlReflectObjectProperty)
{
write(JSGoogEmitterTokens.GOOG_REFLECT_OBJECTPROPERTY);
write(ASEmitterTokens.PAREN_OPEN);
}
write(ASEmitterTokens.DOUBLE_QUOTE);
write(s);
write(ASEmitterTokens.DOUBLE_QUOTE);
if(useMxmlReflectObjectProperty)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.PAREN_CLOSE);
}
writeNewline(ASEmitterTokens.COMMA);
}
IExpressionNode destNode = bi.getExpressionNodeForDestination();
s = bi.getDestinationString();
if (!allowDynamicBindings && destNode == null && s != null)
{
StringBuilder sb = new StringBuilder();
sb.append("/** @this {");
sb.append(formatQualifiedName(cname));
sb.append("} */\n");
sb.append("function(value) { ");
sb.append("this.");
sb.append(s);
sb.append(" = value; },");
writeNewline(sb.toString());
s = null;
}
else if (destNode != null && s == null)
{
StringBuilder sb = new StringBuilder();
sb.append(generateSetterFunction(bi, destNode));
write(sb.toString());
writeNewline(ASEmitterTokens.COMMA);
}
else
{
write(ASEmitterTokens.NULL);
writeNewline(ASEmitterTokens.COMMA);
}
if (s == null)
{
write(ASEmitterTokens.NULL);
}
else if (s.contains("."))
{
String[] parts = s.split("\\.");
write(ASEmitterTokens.SQUARE_OPEN);
StringBuilder objString = null;
if (useMxmlReflectObjectProperty)
{
objString = new StringBuilder();
objString.append("this");
}
int n = parts.length;
for (int i = 0; i < n; i++)
{
String part = parts[i];
if (i > 0)
{
write(", ");
}
if(useMxmlReflectObjectProperty)
{
write(JSGoogEmitterTokens.GOOG_REFLECT_OBJECTPROPERTY);
write(ASEmitterTokens.PAREN_OPEN);
}
write(ASEmitterTokens.DOUBLE_QUOTE);
write(part);
write(ASEmitterTokens.DOUBLE_QUOTE);
if(useMxmlReflectObjectProperty)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(objString.toString());
write(ASEmitterTokens.PAREN_CLOSE);
objString.append(".");
objString.append(part);
}
}
write(ASEmitterTokens.SQUARE_CLOSE);
}
else
{
if(useMxmlReflectObjectProperty)
{
write(JSGoogEmitterTokens.GOOG_REFLECT_OBJECTPROPERTY);
write(ASEmitterTokens.PAREN_OPEN);
}
write(ASEmitterTokens.DOUBLE_QUOTE);
write(s);
write(ASEmitterTokens.DOUBLE_QUOTE);
if(useMxmlReflectObjectProperty)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.PAREN_CLOSE);
}
writeNewline(ASEmitterTokens.COMMA);
}
}
Set<Entry<Object, WatcherInfoBase>> watcherChains = bindingDataBase.getWatcherChains();
if (watcherChains != null)
{
int count = watcherChains.size();
if (hadOutput) {
if (count > 0) writeNewline(ASEmitterTokens.COMMA);
else writeNewline();
}
for (Entry<Object, WatcherInfoBase> entry : watcherChains)
{
count--;
WatcherInfoBase watcherInfoBase = entry.getValue();
encodeWatcher(watcherInfoBase);
if (count > 0) writeNewline(ASEmitterTokens.COMMA);
}
} else {
if (hadOutput) writeNewline();
}
write(ASEmitterTokens.SQUARE_CLOSE);
writeNewline(ASEmitterTokens.SEMICOLON);
}
private String generateSetterFunction(BindingInfo bi, IExpressionNode destNode) {
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
StringBuilder sb = new StringBuilder();
sb.append("function (value) { ");
if (destNode instanceof InstructionListNode)
{
sb.append(generateDestExpression(bi));
}
else
{
String body = asEmitter.stringifyNode(destNode);
sb.append(body);
sb.append(" = value;");
}
sb.append("}");
return sb.toString();
}
String generateDestExpression(BindingInfo bi)
{
StringBuilder sb = new StringBuilder();
MXMLBindingNode node = (MXMLBindingNode)bi.node;
IMXMLBindingAttributeNode destNode = node.getDestinationAttributeNode();
Stack<IASNode> nodeStack = new Stack<IASNode>();
nodeStack.push(node);
IASNode parentNode = node.getParent();
while (!(parentNode instanceof IMXMLInstanceNode))
{
nodeStack.push(parentNode);
parentNode = parentNode.getParent();
}
boolean isXML = parentNode instanceof IMXMLXMLNode;
boolean isXMLList = parentNode instanceof IMXMLXMLListNode;
String effectiveID = ((IMXMLInstanceNode)parentNode).getEffectiveID();
sb.append("this.");
sb.append(effectiveID);
// at least for one XMLList case, we could not trust
// the nodestack as children were only binding nodes
// and not preceding XML nodes
if (isXMLList)
{
// re-interpret the instruction list.
// it would not be a surprise of the non-XMLList cases will
// eventually require this code path
InstructionListNode ilNode = (InstructionListNode)destNode.getExpressionNode();
InstructionList il = ilNode.getInstructions();
ArrayList<Instruction> abcs = il.getInstructions();
// the first indexed access accesses an XMLList, the next ones
// access an XML object
boolean indexedAccess = false;
int n = abcs.size();
for (int i = 0; i < n; i++)
{
Instruction inst = abcs.get(i);
int opCode = inst.getOpcode();
if (opCode == ABCConstants.OP_getlocal0)
{
if (i > 0)
System.out.println("unexpected getLocal0 in binding expression");
}
else if (opCode == ABCConstants.OP_getproperty)
{
OneOperandInstruction getProp = (OneOperandInstruction)inst;
Name propName = (Name)getProp.getOperand(0);
if (i == 0)
System.out.println("unexpected opcode in binding expression");
else if (i == 1 && !propName.getBaseName().contentEquals(effectiveID))
System.out.println("unexpected effectiveID in binding expression");
else if (i > 1)
{
try
{
Integer.parseInt(propName.getBaseName());
if (indexedAccess)
sb.append(".children()");
sb.append("[" + propName.getBaseName() + "]" );
indexedAccess = true;
} catch (NumberFormatException ex)
{
sb.append(".elements(" + propName.getBaseName() + ")" );
}
}
}
else if (opCode == ABCConstants.OP_getlocal1)
{
if (i != n - 3)
System.out.println("unexpected getLocal1 in binding expression");
}
else if (opCode == ABCConstants.OP_setproperty)
{
if (i != n - 2)
System.out.println("unexpected setProperty in binding expression");
OneOperandInstruction setProp = (OneOperandInstruction)inst;
Name propName = (Name)setProp.getOperand(0);
if (!propName.getBaseName().contentEquals(destNode.getName()))
System.out.println("unexpected setProperty name in binding expression");
break; // exit loop
}
}
sb.append(".setAttribute('" + destNode.getName() + "', value);" );
}
else
{
while (nodeStack.size() > 0)
{
IASNode childNode = nodeStack.pop();
int n = parentNode.getChildCount();
int i = 0;
for (; i < n; i++)
{
if (childNode == parentNode.getChild(i))
break;
}
assert i < n;
sb.append("[" + new Integer(i).toString() + "]" );
parentNode = childNode;
}
if (isXML)
sb.append(".setAttribute('" + destNode.getName() + "', value);" );
else
sb.append("." + destNode.getName() + " = value;");
}
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() + ASEmitterTokens.COMMA.getToken());
IExpressionNode params[] = functionWatcherInfo.params;
StringBuilder sb = new StringBuilder();
sb.append("function() { return [");
boolean firstone = true;
for (IExpressionNode param : params)
{
if (!firstone)
{
sb.append(ASEmitterTokens.COMMA.getToken());
}
firstone = false;
sb.append(asEmitter.stringifyNode(param));
}
sb.append("]; },");
writeNewline(sb.toString());
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
StringBuilder propertyGetterFunction = null;
if (watcherInfoBase.isRoot && !makeStaticWatcher)
{
// TODO: figure out what this looks like
// propertyGetterFunction = this.propertyGetter;
// assert propertyGetterFunction != null;
StringBuilder sb = new StringBuilder();
sb.append("function() { return this.");
RoyaleJSProject fjp = (RoyaleJSProject) getMXMLWalker().getProject();
String propName = propertyWatcherInfo.getPropertyName();
IDefinitionSet defSet = this.classDefinition.getContainedScope().getLocalDefinitionSetByName(propName);
if (defSet != null)
{
IDefinition rootDef = defSet.getDefinition(0);
if (rootDef != null)
{
if (rootDef.isPrivate())
{
sb.append(((JSRoyaleEmitter)asEmitter).formatPrivateName(this.classDefinition.getQualifiedName(),
propName));
sb.append("; }");
propertyGetterFunction = sb;
}
// might need for public as well.
}
}
}
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
else
writeNewline(propertyGetterFunction.toString() + ASEmitterTokens.COMMA.getToken());
if (type == WatcherType.STATIC_PROPERTY)
{
StaticPropertyWatcherInfo pwinfo = (StaticPropertyWatcherInfo)watcherInfoBase;
Name classMName = pwinfo.getContainingClass(getMXMLWalker().getProject());
writeNewline(nameToString(classMName)+ ASEmitterTokens.COMMA.getToken());
}
}
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(ASEmitterTokens.COMMA);
}
write("null" + ASEmitterTokens.SQUARE_CLOSE.getToken() );
}
else
{
write("null" );
}
}
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 (IMXMLScriptNode node : scripts)
{
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
int len = node.getChildCount();
if (len > 0)
{
for (int i = 0; i < len; i++)
{
IASNode cnode = node.getChild(i);
if (cnode.getNodeID() == ASTNodeID.VariableID) {
((JSRoyaleEmitter) asEmitter).getModel().getVars().add((IVariableNode) cnode);
} else {
if (cnode.getNodeID() == ASTNodeID.BindableVariableID) {
IVariableNode variableNode = (IVariableNode) cnode;
BindableVarInfo bindableVarInfo = new BindableVarInfo();
bindableVarInfo.isStatic = variableNode.hasModifier(ASModifier.STATIC);;
bindableVarInfo.namespace = variableNode.getNamespace();
IMetaTagsNode metaTags = variableNode.getMetaTags();
if (metaTags != null) {
IMetaTagNode[] tags = metaTags.getAllTags();
if (tags.length > 0)
bindableVarInfo.metaTags = tags;
}
String bindableVarType = "*";
IExpressionNode variableTypeNode = variableNode.getVariableTypeNode();
if (variableTypeNode != null)
{
bindableVarType = variableTypeNode.resolveType(getMXMLWalker().getProject()).getQualifiedName();
}
bindableVarInfo.type = bindableVarType;
((JSRoyaleEmitter) asEmitter).getModel().getBindableVars().put(variableNode.getName(), bindableVarInfo);
}
}
if (!(cnode instanceof IImportNode))
{
asEmitter.getWalker().walk(cnode);
write(ASEmitterTokens.SEMICOLON.getToken());
if (i == len - 1)
indentPop();
writeNewline();
writeNewline();
writeNewline();
}
}
}
}
}
//--------------------------------------------------------------------------
protected void emitEvents(String cname)
{
for (MXMLEventSpecifier event : events)
{
writeNewline("/**");
if (emitExports)
writeNewline(" * @export");
writeNewline(" * @param {" + formatQualifiedName(event.type) + "} event");
writeNewline(" */");
writeNewline(formatQualifiedName(cname)
+ ".prototype." + event.eventHandler + " = function(event)");
writeNewline(ASEmitterTokens.BLOCK_OPEN, true);
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
IMXMLEventSpecifierNode node = event.node;
int len = node.getChildCount();
for (int i = 0; i < len; i++)
{
if (i > 0)
{
writeNewline();
}
IASNode cnode = node.getChild(i);
asEmitter.getWalker().walk(cnode);
write(ASEmitterTokens.SEMICOLON);
}
indentPop();
writeNewline();
write(ASEmitterTokens.BLOCK_CLOSE);
writeNewline(ASEmitterTokens.SEMICOLON);
writeNewline();
writeNewline();
}
}
//--------------------------------------------------------------------------
private boolean skippedDefineProps;
protected void emitPropertyGetterSetters(String cname)
{
int n = 0;
for (MXMLDescriptorSpecifier instance : instances)
{
if (instance.id != null || instance.hasLocalId)
{
n++;
}
}
if (n == 0 && (descriptorTree.size() == 0 ||
descriptorTree.size() == 1 && descriptorTree.get(0).propertySpecifiers.size() == 0))
{
skippedDefineProps = true;
return;
}
String formattedCName = formatQualifiedName(cname);
write(JSGoogEmitterTokens.OBJECT);
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSEmitterTokens.DEFINE_PROPERTIES);
write(ASEmitterTokens.PAREN_OPEN);
write(formattedCName);
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSEmitterTokens.PROTOTYPE);
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write("/** @lends {");
write(formattedCName);
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSEmitterTokens.PROTOTYPE);
write("} */ {");
indentPush();
writeNewline();
int i = 0;
for (MXMLDescriptorSpecifier instance : instances)
{
String instanceId = instance.id;
if (instanceId == null && instance.hasLocalId ){
instanceId = instance.effectiveId;
}
if (instanceId != null)
{
indentPush();
writeNewline(instanceId + ": {");
writeNewline("/** @this {" + formattedCName + "} */");
indentPush();
writeNewline("get: function() {");
indentPop();
writeNewline("return this." + instanceId + "_;");
writeNewline("},");
writeNewline("/** @this {" + formattedCName + "} */");
indentPush();
writeNewline("set: function(value) {");
indentPush();
writeNewline("if (value != this." + instanceId + "_) {");
writeNewline("this." + instanceId + "_ = value;");
write("this.dispatchEvent(org.apache.royale.events.ValueChangeEvent.createUpdateEvent(this, '");
indentPop();
writeNewline(instanceId + "', 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)
{
RoyaleJSProject project = (RoyaleJSProject) getMXMLWalker().getProject();
project.needLanguage = true;
MXMLDescriptorSpecifier root = descriptorTree.get(0);
if (root.propertySpecifiers.size() == 0 && skippedDefineProps)
return; // all declarations were primitives
root.isTopNode = false;
collectExportedNames(root);
indentPush();
writeNewline("'MXMLDescriptor': {");
writeNewline("/** @this {" + formatQualifiedName(cname) + "} */");
indentPush();
writeNewline("get: function() {");
writeNewline("if (this.mxmldd == undefined)");
indentPush();
writeNewline("{");
writeNewline("/** @type {Array} */");
writeNewline("var arr = " + formatQualifiedName(cname) + ".superClass_.get__MXMLDescriptor.apply(this);");
writeNewline("/** @type {Array} */");
indentPush();
writeNewline("var mxmldd = [");
mxmlDescriptorEmitter.emit(root);
indentPop();
writeNewline();
writeNewline("];");
indentPush();
writeNewline("if (arr)");
indentPop();
writeNewline("this.mxmldd = arr.concat(mxmldd);");
indentPush();
writeNewline("else");
indentPop();
indentPop();
writeNewline("this.mxmldd = mxmldd;");
writeNewline("}");
indentPop();
writeNewline("return this.mxmldd;");
indentPop();
writeNewline("}");
indentPop();
writeNewline("}");
indentPop();
writeNewline("});");
}
}
private void collectExportedNames(MXMLDescriptorSpecifier descriptor)
{
ICompilerProject project = getMXMLWalker().getProject();
RoyaleJSProject royaleProject = null;
if (project instanceof RoyaleJSProject)
{
royaleProject = (RoyaleJSProject) project;
String name = descriptor.name;
if (name == null)
name = this.classDefinition.getQualifiedName();
for (MXMLDescriptorSpecifier prop : descriptor.propertySpecifiers)
{
String propName = prop.name;
royaleProject.addExportedName(/*name + "." + */propName);
if (prop.propertySpecifiers.size() > 0)
{
collectExportedNames(prop.propertySpecifiers.get(0));
}
}
if (descriptor.childrenSpecifier != null)
{
for (MXMLDescriptorSpecifier prop : descriptor.childrenSpecifier.propertySpecifiers)
{
collectExportedNames(prop);
}
}
}
}
//--------------------------------------------------------------------------
private HashMap<IMXMLEventSpecifierNode, String> eventHandlerNameMap = new HashMap<IMXMLEventSpecifierNode, String>();
@Override
public void emitEventSpecifier(IMXMLEventSpecifierNode node)
{
IMXMLStateNode currentState = null;
if (!inStatesOverride.empty())
currentState = inStatesOverride.peek();
if (isStateDependent(node, currentState, true))
return;
IDefinition cdef = node.getDefinition();
MXMLDescriptorSpecifier currentDescriptor = getCurrentDescriptor("i");
MXMLEventSpecifier eventSpecifier = new MXMLEventSpecifier();
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker()).getASEmitter();
JSRoyaleEmitter fjs = (JSRoyaleEmitter)asEmitter;
IClassDefinition currentClass = fjs.getModel().getCurrentClass();
//naming needs to avoid conflicts with ancestors - using delta from object which is
//a) short and b)provides a 'unique' (not zero risk, but very low risk) option
String nameBase = EmitterUtils.getClassDepthNameBase(MXMLRoyaleEmitterTokens.EVENT_PREFIX
.getToken(), currentClass, getMXMLWalker().getProject());
eventSpecifier.eventHandler = nameBase + eventCounter++;
eventSpecifier.name = cdef.getBaseName();
eventSpecifier.type = node.getEventParameterDefinition()
.getTypeAsDisplayString();
eventHandlerNameMap.put(node, eventSpecifier.eventHandler);
//save the node for emitting later in emitEvents()
//previously, we stringified the node and saved that instead of the
//node, but source maps don't work when you stringify a node too early -JT
eventSpecifier.node = node;
if (currentDescriptor != null)
currentDescriptor.eventSpecifiers.add(eventSpecifier);
else if (inStatesOverride.empty()) // in theory, if no currentdescriptor must be top tag event
propertiesTree.eventSpecifiers.add(eventSpecifier);
events.add(eventSpecifier);
}
@Override
public void emitInstance(IMXMLInstanceNode node)
{
IMXMLStateNode currentState = null;
if (!inStatesOverride.empty())
currentState = inStatesOverride.peek();
if (overrideInstanceToEmit != node && isStateDependent(node, currentState, false))
return;
ASTNodeID nodeID = node.getNodeID();
if ((nodeID == ASTNodeID.MXMLXMLID || nodeID == ASTNodeID.MXMLXMLListID) &&
node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
IClassDefinition cdef = node
.getClassReference((ICompilerProject) getMXMLWalker()
.getProject());
MXMLDescriptorSpecifier currentPropertySpecifier = getCurrentDescriptor("ps");
if (nodeID == ASTNodeID.MXMLFunctionID)
{
RoyaleJSProject project = (RoyaleJSProject) getMXMLWalker().getProject();
project.needLanguage = true;
MXMLFunctionNode fnode = ((MXMLFunctionNode)node);
IExpressionNode fexpNode = (IExpressionNode)fnode.getExpressionNode();
IASEmitter asEmitter = ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
String fNodeString = ((JSRoyaleEmitter)asEmitter).stringifyNode(fexpNode);
currentPropertySpecifier.value = fNodeString;
return;
}
String effectiveId = null;
String id = node.getID();
if (id == null)
{
effectiveId = node.getEffectiveID();
if (effectiveId == null)
effectiveId = node.getClassDefinitionNode().getGeneratedID(node);
}
MXMLDescriptorSpecifier currentInstance = new MXMLDescriptorSpecifier();
currentInstance.isProperty = false;
currentInstance.id = id;
currentInstance.hasLocalId = node.getLocalID() != null;
currentInstance.effectiveId = effectiveId;
currentInstance.name = formatQualifiedName(cdef.getQualifiedName());
currentInstance.parent = currentPropertySpecifier;
if (currentPropertySpecifier != null)
currentPropertySpecifier.propertySpecifiers.add(currentInstance);
else if (inMXMLContent)
descriptorTree.add(currentInstance);
else
{
// we get here if a instance is a child of a top-level tag
// and there is no default property. If there are other
// ways to get here, then the code will need adjusting.
// this code assumes that the children will have an id
// and will just create properties with the children's id
// on the class.
MXMLDescriptorSpecifier prop = new MXMLDescriptorSpecifier();
prop.isProperty = true;
prop.name = id;
prop.parent = propertiesTree;
propertiesTree.propertySpecifiers.add(prop);
currentInstance.parent = prop;
prop.propertySpecifiers.add(currentInstance);
}
addInstanceIfNeeded(instances, 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)
{
inStatesOverride.push(stateNode);
for (int i=0; i<snodes.size(); i++)
{
IMXMLNode inode = snodes.get(i);
if (inode.getNodeID() == ASTNodeID.MXMLInstanceID)
{
emitInstanceOverride((IMXMLInstanceNode)inode, stateNode);
}
}
// 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;
}
}
}
inStatesOverride.pop();
}
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);
}
}
private void addInstanceIfNeeded(
ArrayList<MXMLDescriptorSpecifier> instances2,
MXMLDescriptorSpecifier currentInstance) {
for (MXMLDescriptorSpecifier instance : instances2)
if (instance.id != null && currentInstance.id != null && instance.id.equals(currentInstance.id))
return;
instances.add(currentInstance);
}
public void emitPropertyOverride(IMXMLPropertySpecifierNode propertyNode)
{
RoyaleProject project = (RoyaleProject) 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)
{
RoyaleProject project = (RoyaleProject) 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 = MXMLRoyaleEmitterTokens.BINDING_PREFIX.getToken() + bindingCounter++;
setProp.id = overrideID;
instances.add(setProp);
IRoyaleProject project = (IRoyaleProject)(walker.getProject());
BindingDatabase bd = project.getBindingMap().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)
{
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("ps");
RoyaleProject project = (RoyaleProject) 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 = JSRoyaleEmitterTokens.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);
}
public void emitInstanceOverride(IMXMLInstanceNode instanceNode, IMXMLStateNode state)
{
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("ps");
RoyaleProject project = (RoyaleProject) 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;
overrideInstanceToEmit = instanceNode;
getMXMLWalker().walk(instanceNode); // instance node
overrideInstanceToEmit = null;
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, state, true))
{
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);
}
}
private String nameToString(Name name)
{
String s;
Namespace ns = name.getSingleQualifier();
s = ns.getName();
if (s != "") s = s + ASEmitterTokens.MEMBER_ACCESS.getToken() + name.getBaseName();
else s = name.getBaseName();
return s;
}
/**
* Determines whether a string matches a state's name or group name.
*/
protected boolean inStateOrStateGroup(String name, IMXMLStateNode state)
{
if (state == null) return false;
if (name.contentEquals(state.getStateName()))
return true;
String[] groups = state.getStateGroups();
if (groups != null)
{
for (String s : groups)
{
if (name.contentEquals(s))
return true;
}
}
return false;
}
/**
* Determines whether a node is state-dependent.
* TODO: we should move to IMXMLNode
*/
protected boolean isStateDependent(IASNode node, IMXMLStateNode currentState, boolean includeGroups)
{
if (node instanceof IMXMLSpecifierNode)
{
String suffix = ((IMXMLSpecifierNode)node).getSuffix();
return suffix != null && suffix.length() > 0 && !inStateOrStateGroup(suffix, currentState);
}
else if (isStateDependentInstance(node, currentState, includeGroups))
return true;
return false;
}
/**
* Determines whether the geven node is an instance node, as is state dependent
*/
protected boolean isStateDependentInstance(IASNode node, IMXMLStateNode currentState, boolean includeGroups)
{
if (node instanceof IMXMLInstanceNode)
{
String[] includeIn = ((IMXMLInstanceNode)node).getIncludeIn();
String[] excludeFrom = ((IMXMLInstanceNode)node).getExcludeFrom();
if (includeGroups)
{
if (includeIn != null && currentState != null)
for (String s : includeIn)
if (inStateOrStateGroup(s, currentState)) return false;
if (excludeFrom != null && currentState != null)
{
for (String s : excludeFrom)
if (inStateOrStateGroup(s, currentState)) return true;
return false;
}
}
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, null, true))
return;
IDefinition cdef = node.getDefinition();
IASNode cnode = node.getChild(0);
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i");
MXMLDescriptorSpecifier currentPropertySpecifier = new MXMLDescriptorSpecifier();
currentPropertySpecifier.isProperty = true;
currentPropertySpecifier.name = cdef != null ? cdef.getQualifiedName() : node.getName();
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(false, null, currentPropertySpecifier);
getMXMLWalker().walk(cnode); // Array or Instance
moveUp(false, false);
inMXMLContent = oldInMXMLContent;
}
@Override
public void emitScript(IMXMLScriptNode node)
{
//save the script for emitting later in emitScripts()
//previously, we stringified the node and saved that instead of the
//node, but source maps don't work when you stringify a node too early -JT
scripts.add(node);
}
@Override
public void emitStyleSpecifier(IMXMLStyleSpecifierNode node)
{
}
//--------------------------------------------------------------------------
@Override
public void emitObject(IMXMLObjectNode node)
{
final int len = node.getChildCount();
if (!makingSimpleArray)
{
MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps");
//GD - hasArray below allows a dataProvider with <fx:Object's>
if (ps.hasObject || ps.hasArray || ps.parent == null) //('ps.parent == null' was added to allow a top level fx:Object definition, they were not being output without that)
{
emitInstance(node);
return;
}
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)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
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();
//a single <fx:Object> inside an array also makes it non-simple (@todo test mixed simple and non-simple)
if (nodeID == ASTNodeID.MXMLArrayID || nodeID == ASTNodeID.MXMLInstanceID || nodeID == ASTNodeID.MXMLObjectID || 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;
}
@Override
public void emitBoolean(IMXMLBooleanNode node)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
super.emitBoolean(node);
}
@Override
public void emitNumber(IMXMLNumberNode node)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
super.emitNumber(node);
}
@Override
public void emitInt(IMXMLIntNode node)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
super.emitInt(node);
}
@Override
public void emitUint(IMXMLUintNode node)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
super.emitUint(node);
}
@Override
public void emitString(IMXMLStringNode node)
{
if (node.getParent().getNodeID() == ASTNodeID.MXMLDeclarationsID)
{
primitiveDeclarationNodes.add(node);
return;
}
getCurrentDescriptor("ps").valueNeedsQuotes = true;
emitAttributeValue(node);
}
@Override
public void emitMXMLClass(IMXMLClassNode node)
{
RoyaleJSProject project = (RoyaleJSProject)getMXMLWalker().getProject();
ITypeDefinition cdef = node.getValue(project);
String qname = formatQualifiedName(cdef.getQualifiedName());
ICompilationUnit classCU = project.resolveQNameToCompilationUnit(qname);
ICompilationUnit cu = project.resolveQNameToCompilationUnit(classDefinition.getQualifiedName());
project.addDependency(cu, classCU, DependencyType.EXPRESSION, qname);
MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps");
ps.value = qname;
}
//--------------------------------------------------------------------------
@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());
}
s = s.replace("\r\n", "\\n");
s = s.replace("\n", "\\n");
ps.value += s;
if (ps.valueNeedsQuotes)
ps.value += ASEmitterTokens.SINGLE_QUOTE.getToken();
}
//--------------------------------------------------------------------------
@Override
public void emitFactory(IMXMLFactoryNode node)
{
IASNode cnode = node.getChild(0);
ITypeDefinition type = ((IMXMLClassNode)cnode).getValue(getMXMLWalker().getProject());
if (type == null) return;
MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps");
ps.value = "new " + formatQualifiedName("org.apache.royale.core.ClassFactory") + "(";
if (cnode instanceof IMXMLClassNode)
{
ps.value += formatQualifiedName(type.getQualifiedName());
}
ps.value += ")";
}
//--------------------------------------------------------------------------
@Override
public void emitComponent(IMXMLComponentNode node)
{
MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps");
ps.value = "new " + formatQualifiedName("org.apache.royale.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();
((JSRoyaleEmitter)asEmitter).setBufferWrite(value);
}
//--------------------------------------------------------------------------
// JS output
//--------------------------------------------------------------------------
private void emitHeader(IMXMLDocumentNode node)
{
String cname = node.getFileNode().getName();
String bcname = node.getBaseClassName();
RoyaleJSProject project = (RoyaleJSProject) 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 Royale Compiler from " + sourceName.replace('\\', '/'));
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);
// TODO (mschmalle) will remove this cast as more things get abstracted
JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
if (fjs.getModel().hasStaticBindableVars()) {
//we need to add EventDispatcher
if (deps.indexOf(BindableEmitter.DISPATCHER_CLASS_QNAME) == -1)
deps.add(BindableEmitter.DISPATCHER_CLASS_QNAME);
if (usedNames.indexOf(BindableEmitter.DISPATCHER_CLASS_QNAME) == -1)
usedNames.add(BindableEmitter.DISPATCHER_CLASS_QNAME);
}
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 RoyaleJSProject)
{
if (((RoyaleJSProject)project).needLanguage)
emitHeaderLine(JSRoyaleEmitterTokens.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);
}
private String createRequireLine(String qname, boolean isProvide) {
StringBuilder createHeader = new StringBuilder();
createHeader.append(isProvide ? JSGoogEmitterTokens.GOOG_PROVIDE.getToken() : JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
createHeader.append(ASEmitterTokens.PAREN_OPEN.getToken());
createHeader.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
createHeader.append(qname);
createHeader.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
createHeader.append(ASEmitterTokens.PAREN_CLOSE.getToken());
createHeader.append(ASEmitterTokens.SEMICOLON.getToken());
return createHeader.toString();
}
//--------------------------------------------------------------------------
// 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;
if (index > -1)
currentInstances.remove(index);
}
else
{
index = currentPropertySpecifiers.size() - 1;
if (index > -1)
currentPropertySpecifiers.remove(index);
}
}
}
public String formatQualifiedName(String name)
{
return formatQualifiedName(name, true);
}
protected String formatQualifiedName(String name, boolean useName)
{
/*
if (name.contains("goog.") || name.startsWith("Vector."))
return name;
name = name.replaceAll("\\.", "_");
*/
if (subDocumentNames.contains(name))
return documentDefinition.getQualifiedName() + "." + name;
if (NativeUtils.isJSNative(name)) return name;
if (inStaticInitializer)
{
if (!staticUsedNames.contains(name) && !NativeUtils.isJSNative(name) && isGoogProvided(name))
{
staticUsedNames.add(name);
}
}
if (useName && !usedNames.contains(name) && isGoogProvided(name))
{
usedNames.add(name);
}
return name;
}
@SuppressWarnings("incomplete-switch")
private void emitComplexInitializers(IASNode node)
{
boolean wroteSelf = false;
int n = node.getChildCount();
for (int i = 0; i < n; i++)
{
IASNode child = node.getChild(i);
if (child.getNodeID() == ASTNodeID.MXMLScriptID)
{
int m = child.getChildCount();
for (int j = 0; j < m; j++)
{
IASNode schild = child.getChild(j);
ASTNodeID schildID = schild.getNodeID();
if (schildID == ASTNodeID.VariableID ||
schildID == ASTNodeID.BindableVariableID)
{
IVariableNode varnode = (IVariableNode)schild;
IExpressionNode vnode = varnode.getAssignedValueNode();
if (vnode != null && (!EmitterUtils.isScalar(vnode)))
{
IDefinition varDef = varnode.getDefinition();
if (varDef.isStatic())
{
continue;
}
if(!wroteSelf && vnode instanceof IFunctionObjectNode)
{
writeNewline();
writeToken(ASEmitterTokens.VAR);
writeToken(JSGoogEmitterTokens.SELF);
writeToken(ASEmitterTokens.EQUAL);
write(ASEmitterTokens.THIS);
writeNewline(ASEmitterTokens.SEMICOLON);
wroteSelf = true;
}
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
ICompilerProject project = getMXMLWalker().getProject();
String qname = varnode.getName();
if (varDef != null && varDef.isPrivate() && project.getAllowPrivateNameConflicts())
qname = fjs.formatPrivateName(varDef.getParent().getQualifiedName(), qname);
if (EmitterUtils.isCustomNamespace(varnode.getNamespace())) {
INamespaceDecorationNode ns = ((VariableNode) varnode).getNamespaceNode();
INamespaceDefinition nsDef = (INamespaceDefinition)ns.resolve(project);
fjs.formatQualifiedName(nsDef.getQualifiedName()); // register with used names
String s = nsDef.getURI();
write(JSRoyaleEmitter.formatNamespacedProperty(s, qname, false));
}
else write(qname);
if (schildID == ASTNodeID.BindableVariableID && !varnode.isConst())
write("_"); // use backing variable
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
IDefinition varTypeDef = null;
IExpressionNode variableTypeNode = varnode.getVariableTypeNode();
if (variableTypeNode != null)
{
varTypeDef = variableTypeNode.resolve(getMXMLWalker().getProject());
}
fjs.emitAssignmentCoercion(vnode, varTypeDef);
write(ASEmitterTokens.SEMICOLON);
}
}
}
}
}
n = primitiveDeclarationNodes.size();
for (int i = 0; i < n; i++)
{
IMXMLInstanceNode declNode = primitiveDeclarationNodes.get(i);
ASTNodeID nodeId = declNode.getNodeID();
String varname;
switch (nodeId)
{
case MXMLStringID:
{
IMXMLStringNode stringNode = (IMXMLStringNode)declNode;
IASNode expressionNode = stringNode.getExpressionNode();
// it might be a binding expression instead of a literal
if (expressionNode instanceof IMXMLLiteralNode)
{
varname = stringNode.getEffectiveID();
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
write(varname);
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
IMXMLLiteralNode valueNode = (IMXMLLiteralNode) expressionNode;
Object value = valueNode.getValue();
write(objectToString(value));
write(ASEmitterTokens.SEMICOLON);
}
break;
}
case MXMLArrayID:
{
IMXMLArrayNode arrayNode = (IMXMLArrayNode)declNode;
int m = arrayNode.getChildCount();
boolean isDataBinding = false;
if(m == 1)
{
IASNode child = arrayNode.getChild(0);
isDataBinding = child instanceof IMXMLDataBindingNode;
}
if (!isDataBinding)
{
varname = arrayNode.getEffectiveID();
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
write(varname);
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
write(ASEmitterTokens.SQUARE_OPEN);
boolean firstOne = true;
for (int j = 0; j < m; j++)
{
IMXMLInstanceNode valueNode = (IMXMLInstanceNode)(arrayNode.getChild(j));
if (firstOne)
firstOne = false;
else
writeToken(ASEmitterTokens.COMMA);
write(instanceToString(valueNode));
}
write(ASEmitterTokens.SQUARE_CLOSE);
write(ASEmitterTokens.SEMICOLON);
}
break;
}
case MXMLXMLID:
{
IMXMLXMLNode xmlNode = (IMXMLXMLNode)declNode;
String valueString = xmlNode.getXMLString();
if (valueString != null)
{
varname = xmlNode.getEffectiveID();
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
write(varname);
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
write("new XML('");
write(StringEscapeUtils.escapeJavaScript(valueString));
write("')");
write(ASEmitterTokens.SEMICOLON);
}
break;
}
case MXMLXMLListID:
{
IMXMLXMLListNode xmlNode = (IMXMLXMLListNode)declNode;
String valueString = xmlNode.getXMLString();
if (valueString != null)
{
varname = xmlNode.getEffectiveID();
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
write(varname);
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
write("new XMLList('");
write(StringEscapeUtils.escapeJavaScript(valueString));
write("')");
write(ASEmitterTokens.SEMICOLON);
}
break;
}
case MXMLBooleanID:
case MXMLNumberID:
case MXMLIntID:
case MXMLUintID:
{
IMXMLExpressionNode scalarNode = (IMXMLExpressionNode)declNode;
IASNode expressionNode = scalarNode.getExpressionNode();
// it might be a binding expression instead of a literal
if (expressionNode instanceof IMXMLLiteralNode)
{
varname = scalarNode.getEffectiveID();
writeNewline();
write(ASEmitterTokens.THIS);
write(ASEmitterTokens.MEMBER_ACCESS);
write(varname);
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
IMXMLLiteralNode valueNode = (IMXMLLiteralNode) expressionNode;
Object value = valueNode.getValue();
write(value.toString());
write(ASEmitterTokens.SEMICOLON);
}
break;
}
}
}
}
private String objectToString(Object value)
{
if (value instanceof String)
{
String s = (String)value;
s = StringEscapeUtils.escapeJavaScript(s);
return "'" + s + "'";
}
return "";
}
private String instanceToString(IMXMLInstanceNode instanceNode)
{
if (instanceNode instanceof IMXMLStringNode)
{
IMXMLStringNode stringNode = (IMXMLStringNode)instanceNode;
IASNode vNode = stringNode.getExpressionNode();
if (vNode instanceof IMXMLLiteralNode)
{
IMXMLLiteralNode valueNode = (IMXMLLiteralNode)vNode;
Object value = valueNode.getValue();
return objectToString(value);
}
else
{
return "''";
}
}
else if (instanceNode instanceof IMXMLObjectNode)
{
IMXMLObjectNode objectNode = (IMXMLObjectNode)instanceNode;
StringBuilder sb = new StringBuilder();
sb.append(ASEmitterTokens.BLOCK_OPEN.getToken());
int m = objectNode.getChildCount();
boolean firstOne = true;
for (int j = 0; j < m; j++)
{
if (firstOne)
{
firstOne = false;
}
else
{
sb.append(ASEmitterTokens.COMMA.getToken());
sb.append(ASEmitterTokens.SPACE.getToken());
}
IMXMLPropertySpecifierNode propName = (IMXMLPropertySpecifierNode)objectNode.getChild(j);
sb.append(propName.getName());
sb.append(ASEmitterTokens.COLON.getToken());
sb.append(ASEmitterTokens.SPACE.getToken());
IMXMLInstanceNode valueNode = propName.getInstanceNode();
sb.append(instanceToString(valueNode));
}
sb.append(ASEmitterTokens.BLOCK_CLOSE.getToken());
return sb.toString();
}
else if (instanceNode instanceof IMXMLArrayNode)
{
IMXMLArrayNode arrayNode = (IMXMLArrayNode)instanceNode;
StringBuilder sb = new StringBuilder();
sb.append(ASEmitterTokens.SQUARE_OPEN.getToken());
int m = arrayNode.getChildCount();
boolean firstOne = true;
for (int j = 0; j < m; j++)
{
IMXMLInstanceNode valueNode = (IMXMLInstanceNode)(arrayNode.getChild(j));
if (firstOne)
{
firstOne = false;
}
else
{
sb.append(ASEmitterTokens.COMMA.getToken());
sb.append(ASEmitterTokens.SPACE.getToken());
}
sb.append(instanceToString(valueNode));
}
sb.append(ASEmitterTokens.SQUARE_CLOSE.getToken());
return sb.toString();
}
else if ((instanceNode instanceof IMXMLIntNode) ||
(instanceNode instanceof IMXMLUintNode) ||
(instanceNode instanceof IMXMLNumberNode) ||
(instanceNode instanceof IMXMLBooleanNode))
{
IMXMLExpressionNode scalarNode = (IMXMLExpressionNode)instanceNode;
IASNode vNode = scalarNode.getExpressionNode();
if (vNode instanceof IMXMLLiteralNode)
{
IMXMLLiteralNode valueNode = (IMXMLLiteralNode)vNode;
Object value = valueNode.getValue();
return value.toString();
}
}
return "";
}
public void emitComplexStaticInitializers(IASNode node){
JSRoyaleEmitter fjs = (JSRoyaleEmitter) ((IMXMLBlockWalker) getMXMLWalker())
.getASEmitter();
if (!fjs.getFieldEmitter().hasComplexStaticInitializers) return;
int n = node.getChildCount();
boolean sawOutput = false;
for (int i = 0; i < n; i++)
{
IASNode child = node.getChild(i);
if (child.getNodeID() == ASTNodeID.MXMLScriptID)
{
int m = child.getChildCount();
for (int j = 0; j < m; j++)
{
IASNode schild = child.getChild(j);
ASTNodeID schildID = schild.getNodeID();
if (schildID == ASTNodeID.VariableID ||
schildID == ASTNodeID.BindableVariableID)
{
sawOutput = fjs.getFieldEmitter().emitFieldInitializer((IVariableNode) schild) || sawOutput;
}
}
}
}
if (sawOutput) {
writeNewline();
writeNewline();
}
}
@Override
public void emitImplements(IMXMLImplementsNode node)
{
StringBuilder list = new StringBuilder();
boolean needsComma = false;
IIdentifierNode[] interfaces = node.getInterfaceNodes();
for (IIdentifierNode iface : interfaces)
{
if (needsComma)
list.append(", ");
list.append(iface.getName());
needsComma = true;
}
//System.out.println("mxml implements "+list);
interfaceList = list.toString();
}
boolean isGoogProvided(String className)
{
ICompilerProject project = getMXMLWalker().getProject();
return ((RoyaleJSProject)project).isGoogProvided(className);
}
@Override
public void emitRemoteObjectMethod(IMXMLRemoteObjectMethodNode node) {
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i");
String propName = null;
int l = node.getChildCount();
for (int k = 0; k < l; k++)
{
IASNode child = node.getChild(k);
if (child.getNodeID() == ASTNodeID.MXMLPropertySpecifierID)
{
IMXMLPropertySpecifierNode propNode = (IMXMLPropertySpecifierNode)child;
if (propNode.getName().equals("name"))
{
// assume StringNode with LiteralNode
IMXMLStringNode literalNode = (IMXMLStringNode)propNode.getChild(0);
propName = literalNode.getValue();
break;
}
}
}
MXMLDescriptorSpecifier propertySpecifier = new MXMLDescriptorSpecifier();
propertySpecifier.isProperty = true;
propertySpecifier.name = propName;
propertySpecifier.parent = currentInstance;
currentInstance.propertySpecifiers.add(propertySpecifier);
moveDown(false, null, propertySpecifier);
emitInstance(node);
moveUp(false, false);
// build out the argument list if any
int n = node.getChildCount();
for (int i = 0; i < n; i++)
{
IASNode childNode = node.getChild(i);
if (childNode.getNodeID() == ASTNodeID.MXMLPropertySpecifierID)
{
IMXMLPropertySpecifierNode propNode = (IMXMLPropertySpecifierNode)childNode;
if (propNode.getName().equals("arguments"))
{
ArrayList<String> argList = new ArrayList<String>();
childNode = propNode.getChild(0); // this is an MXMLObjectNode
n = childNode.getChildCount();
for (i = 0; i < n; i++)
{
IASNode argNode = childNode.getChild(i);
propNode = (IMXMLPropertySpecifierNode)argNode;
argList.add(propNode.getName());
}
if (argList.size() > 0)
{
StringBuilder list = new StringBuilder();
list.append("[");
int m = argList.size();
for (int j = 0; j < m; j++)
{
if (j > 0)
list.append(",");
list.append("'" + argList.get(j) + "'");
}
list.append("]");
MXMLDescriptorSpecifier operationInstance = propertySpecifier.propertySpecifiers.get(0);
MXMLDescriptorSpecifier argListSpecifier = new MXMLDescriptorSpecifier();
argListSpecifier.isProperty = true;
argListSpecifier.name = "argumentNames";
argListSpecifier.parent = operationInstance;
argListSpecifier.value = list.toString();
if (operationInstance != null)
operationInstance.propertySpecifiers.add(argListSpecifier);
}
break;
}
}
}
}
@Override
public void emitRemoteObject(IMXMLRemoteObjectNode node) {
emitInstance(node);
// now search for Operations, and add an Object that contains them
int n = node.getChildCount();
MXMLDescriptorSpecifier objectSpecifier = null;
MXMLDescriptorSpecifier propertySpecifier = null;
for (int i = 0; i < n; i++)
{
IASNode child = node.getChild(i);
if (child.getNodeID() == ASTNodeID.MXMLRemoteObjectMethodID)
{
MXMLDescriptorSpecifier currentPropertySpecifier = getCurrentDescriptor("ps");
MXMLDescriptorSpecifier currentInstance =
currentPropertySpecifier.propertySpecifiers.get(currentPropertySpecifier.propertySpecifiers.size() - 1);
if (objectSpecifier == null)
{
propertySpecifier = new MXMLDescriptorSpecifier();
propertySpecifier.isProperty = true;
propertySpecifier.name = "operations";
propertySpecifier.parent = currentInstance;
if (currentInstance != null)
currentInstance.propertySpecifiers.add(propertySpecifier);
objectSpecifier = new MXMLDescriptorSpecifier();
objectSpecifier.isProperty = false;
objectSpecifier.name = formatQualifiedName(IASLanguageConstants.Object);
objectSpecifier.parent = propertySpecifier;
propertySpecifier.propertySpecifiers.add(objectSpecifier);
instances.add(objectSpecifier);
}
moveDown(false, objectSpecifier, null);
getMXMLWalker().walk(child); // RemoteObjectMethod
moveUp(false, true);
}
}
}
@Override
public void emitWebServiceMethod(IMXMLWebServiceOperationNode node) {
MXMLDescriptorSpecifier currentInstance = getCurrentDescriptor("i");
String propName = null;
int l = node.getChildCount();
for (int k = 0; k < l; k++)
{
IASNode child = node.getChild(k);
if (child.getNodeID() == ASTNodeID.MXMLPropertySpecifierID)
{
IMXMLPropertySpecifierNode propNode = (IMXMLPropertySpecifierNode)child;
if (propNode.getName().equals("name"))
{
// assume StringNode with LiteralNode
IMXMLStringNode literalNode = (IMXMLStringNode)propNode.getChild(0);
propName = literalNode.getValue();
break;
}
}
}
MXMLDescriptorSpecifier propertySpecifier = new MXMLDescriptorSpecifier();
propertySpecifier.isProperty = true;
propertySpecifier.name = propName;
propertySpecifier.parent = currentInstance;
currentInstance.propertySpecifiers.add(propertySpecifier);
moveDown(false, null, propertySpecifier);
emitInstance(node);
moveUp(false, false);
// build out the argument list if any
int n = node.getChildCount();
for (int i = 0; i < n; i++)
{
IASNode childNode = node.getChild(i);
if (childNode.getNodeID() == ASTNodeID.MXMLPropertySpecifierID)
{
IMXMLPropertySpecifierNode propNode = (IMXMLPropertySpecifierNode)childNode;
if (propNode.getName().equals("arguments"))
{
ArrayList<String> argList = new ArrayList<String>();
childNode = propNode.getChild(0); // this is an MXMLObjectNode
n = childNode.getChildCount();
for (i = 0; i < n; i++)
{
IASNode argNode = childNode.getChild(i);
propNode = (IMXMLPropertySpecifierNode)argNode;
argList.add(propNode.getName());
}
if (argList.size() > 0)
{
StringBuilder list = new StringBuilder();
list.append("[");
int m = argList.size();
for (int j = 0; j < m; j++)
{
if (j > 0)
list.append(",");
list.append("'" + argList.get(j) + "'");
}
list.append("]");
MXMLDescriptorSpecifier operationInstance = propertySpecifier.propertySpecifiers.get(0);
MXMLDescriptorSpecifier argListSpecifier = new MXMLDescriptorSpecifier();
argListSpecifier.isProperty = true;
argListSpecifier.name = "argumentNames";
argListSpecifier.parent = operationInstance;
argListSpecifier.value = list.toString();
if (operationInstance != null)
operationInstance.propertySpecifiers.add(argListSpecifier);
}
break;
}
}
}
}
@Override
public void emitWebService(IMXMLWebServiceNode node) {
emitInstance(node);
// now search for Operations, and add an Object that contains them
int n = node.getChildCount();
MXMLDescriptorSpecifier objectSpecifier = null;
MXMLDescriptorSpecifier propertySpecifier = null;
for (int i = 0; i < n; i++)
{
IASNode child = node.getChild(i);
if (child.getNodeID() == ASTNodeID.MXMLWebServiceOperationID)
{
MXMLDescriptorSpecifier currentPropertySpecifier = getCurrentDescriptor("ps");
MXMLDescriptorSpecifier currentInstance =
currentPropertySpecifier.propertySpecifiers.get(currentPropertySpecifier.propertySpecifiers.size() - 1);
if (objectSpecifier == null)
{
propertySpecifier = new MXMLDescriptorSpecifier();
propertySpecifier.isProperty = true;
propertySpecifier.name = "operations";
propertySpecifier.parent = currentInstance;
if (currentInstance != null)
currentInstance.propertySpecifiers.add(propertySpecifier);
objectSpecifier = new MXMLDescriptorSpecifier();
objectSpecifier.isProperty = false;
objectSpecifier.name = formatQualifiedName(IASLanguageConstants.Object);
objectSpecifier.parent = propertySpecifier;
propertySpecifier.propertySpecifiers.add(objectSpecifier);
instances.add(objectSpecifier);
}
moveDown(false, objectSpecifier, null);
getMXMLWalker().walk(child); // RemoteObjectMethod
moveUp(false, true);
}
}
}
}