dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,443 lines (1,349 loc) • 72.4 kB
text/typescript
/// <reference path="../types.d.ts" />
/// <reference path="exml_config.ts"/>
import xml = require("../xml/index");
import utils = require("../utils");
import file = require("../FileUtil");
import CodeUtil = require("./code_util");
import exml_config = require("./exml_config");
var compiler: EXMLCompiler;
export function compile(exmlPath: string, srcPath: string): egret.TaskResult {
var result: egret.TaskResult = { exitCode: 0, messages: [] };
exmlPath = exmlPath.split("\\").join("/");
srcPath = srcPath.split("\\").join("/");
var tsPath: string = exmlPath.substring(0, exmlPath.length - 5) + ".g.ts";
if (srcPath.charAt(srcPath.length - 1) != "/") {
srcPath += "/";
}
if (!compiler) {
compiler = new EXMLCompiler();
}
var exitCode = 0, message = "";
if (!file.exists(exmlPath)) {
result.messages.push(utils.tr(2001, exmlPath));
result.exitCode = 2001;
return result;
}
while (true) {
var className: string = exmlPath.substring(srcPath.length, exmlPath.length - 5);
className = className.split("/").join(".");
var xmlString = file.read(exmlPath, true);
try {
var xmlData = xml.parse(xmlString);
}
catch (e) {
message = utils.tr(2002, exmlPath, e.message);
exitCode = 2002;
break;
}
if (!xmlData) {
message = utils.tr(2002, exmlPath, "");
exitCode = 2002;
break
}
try {
var tsText = compiler.compile(xmlData, className, srcPath, exmlPath);
file.save(tsPath, tsText);
}
catch (e) {
message = compiler.errorMessage;
exitCode = compiler.errorCode || 2002;
}
break;
}
if (exitCode) {
file.save(tsPath, "");
}
result.exitCode = exitCode;
result.messages.push(message);
return result;
};
class EXMLCompiler {
/**
* Egret命名空间
*/
public static E: string = "http://ns.egret-labs.org/egret";
/**
* Wing命名空间
*/
public static W: string = "http://ns.egret-labs.org/wing";
private static DECLARATIONS: string = "Declarations";
public errorCode: number;
public errorMessage: string;
/**
* 构造函数
*/
public constructor() {
}
/**
* 配置管理器实例
*/
private exmlConfig: exml_config;
/**
* 获取重复的ID名
*/
public getRepeatedIds(xml: any): Array<any> {
var result: Array<any> = [];
this.getIds(xml, result);
this.repeatedIdDic = {};
return result;
}
private exit(code: number, ...params: string[]) {
this.errorCode = code;
this.errorMessage = utils.tr.apply(utils, [<any>code].concat(params));
throw this.errorMessage;
}
private repeatedIdDic: any = {};
private getIds(xml: any, result: Array<any>): void {
if (xml.namespace != EXMLCompiler.W && xml["$id"]) {
var id: string = xml.$id;
if (this.repeatedIdDic[id]) {
result.push(this.toXMLString(xml));
}
else {
this.repeatedIdDic[id] = true;
}
}
var children: Array<any> = xml.children;
if (children) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var node: any = children[i];
this.getIds(node, result);
}
}
}
/**
* 当前类
*/
private currentClass: CpClass;
/**
* 当前编译的类名
*/
private currentClassName: string;
/**
* 当前要编译的EXML文件
*/
private currentXML: any;
/**
* id缓存字典
*/
private idDic: any;
/**
* 状态代码列表
*/
private stateCode: Array<CpState>;
private stateNames: Array<string>;
/**
* 需要单独创建的实例id列表
*/
private stateIds: Array<any>;
private exmlPath: string = "";
private idToNode: any;
private skinParts: Array<string>;
private toXMLString(node: any): string {
if (!node) {
return "";
}
var str: string = " at <" + node.name;
for (var key in node) {
if (key.charAt(0) == "$") {
var value: string = node[key];
key = key.substring(1);
if (key == "id" && value.substring(0, 2) == "__") {
continue;
}
str += " " + key + "=\"" + value + "\"";
}
}
if (node.isSelfClosing) {
str += "/>";
}
else {
str += ">";
}
return str;
}
/**
* 编译指定的XML对象为TypeScript类。
* 注意:编译前要先注入egret-manifest.xml清单文件给manifestData属性。
* @param xmlData 要编译的EXML文件内容
* @param className 要编译成的完整类名,包括命名空间。
*/
public compile(xmlData: any, className: string, srcPath: string, exmlPath: string): string {
if (!this.exmlConfig) {
this.exmlConfig = exml_config.getInstance();
}
this.exmlPath = exmlPath;
this.currentXML = xmlData;
this.currentClassName = className;
this.exmlConfig.srcPath = srcPath;
this.delayAssignmentDic = {};
this.idDic = {};
this.idToNode = {};
this.stateCode = [];
this.stateNames = [];
this.skinParts = [];
this.declarations = null;
this.currentClass = new CpClass();
this.stateIds = [];
var index: number = className.lastIndexOf(".");
if (index != -1) {
this.currentClass.moduleName = className.substring(0, index);
this.currentClass.className = className.substring(index + 1);
this.currentClass.classPath = className.split(".").join("/") + ".ts";
}
else {
this.currentClass.className = className;
}
this.startCompile();
var resutlCode: string = this.currentClass.toCode();
this.currentClass = null;
return resutlCode;
}
private declarations: any;
/**
* 开始编译
*/
private startCompile(): void {
var result: Array<any> = this.getRepeatedIds(this.currentXML);
if (result.length > 0) {
this.exit(2004, this.exmlPath, result.join("\n"));
}
this.currentClass.superClass = this.getPackageByNode(this.currentXML);
this.getStateNames();
var children: Array<any> = this.currentXML.children;
if (children) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var node: any = children[i];
if (node.namespace == EXMLCompiler.W &&
node.localName == EXMLCompiler.DECLARATIONS) {
this.declarations = node;
break;
}
}
}
var list: Array<string> = [];
this.checkDeclarations(this.declarations, list);
if (list.length > 0) {
this.exit(2020, this.exmlPath, list.join("\n"));
}
if (!this.currentXML.namespace) {
this.exit(2017, this.exmlPath, this.toXMLString(this.currentXML));
}
this.addIds(this.currentXML.children);
this.currentClass.addVariable(new CpVariable("__s", Modifiers.M_PRIVATE, "Function", "egret.gui.setProperties"));
this.createConstructFunc();
}
/**
* 清理声明节点里的状态标志
*/
private checkDeclarations(declarations: any, list: Array<string>): void {
if (!declarations) {
return;
}
var children: Array<any> = declarations.children;
if (children) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var node: any = children[i];
if (node["$includeIn"]) {
list.push(this.toXMLString(node))
delete node.$includeIn;
}
if (node["$excludeFrom"]) {
list.push(this.toXMLString(node))
delete node.$excludeFrom;
}
this.checkDeclarations(node, list);
}
}
}
/**
* 添加必须的id
*/
private addIds(items: Array<any>): void {
if (!items) {
return;
}
var length: number = items.length;
for (var i: number = 0; i < length; i++) {
var node: any = items[i];
if (!node.namespace) {
this.exit(2017, this.exmlPath, this.toXMLString(node));
}
this.addIds(node.children);
if (node.namespace == EXMLCompiler.W) {
}
else if (node["$id"]) {
this.idToNode[node.$id] = node;
if (this.skinParts.indexOf(node.$id) == -1) {
this.skinParts.push(node.$id);
}
this.createVarForNode(node);
if (this.isStateNode(node))//检查节点是否只存在于一个状态里,需要单独实例化
this.stateIds.push(node.$id);
}
else if (node.localName) {
if (this.isProperty(node)) {
var prop: string = node.localName;
var index: number = prop.indexOf(".");
var children: Array<any> = node.children;
if (index == -1 || !children || children.length == 0) {
continue;
}
var firstChild: any = children[0];
this.stateIds.push(firstChild.$id);
}
else {
this.createIdForNode(node);
this.idToNode[node.$id] = node;
if (this.isStateNode(node))
this.stateIds.push(node.$id);
}
}
}
}
/**
* 检测指定节点的属性是否含有视图状态
*/
private containsState(node: any): boolean {
if (node["$includeIn"] || node["$excludeFrom"]) {
return true;
}
for (var name in node) {
if (name.charAt(0) == "$") {
if (name.indexOf(".") != -1) {
return true;
}
}
}
return false;
}
/**
* 为指定节点创建id属性
*/
private createIdForNode(node: any): void {
var idName: string = this.getNodeId(node);
if (this.idDic[idName] == null)
this.idDic[idName] = 1;
else
this.idDic[idName]++;
idName += this.idDic[idName];
node.$id = idName;
}
/**
* 获取节点ID
*/
private getNodeId(node: any): string {
if (node["$id"])
return node.$id;
return "__";
}
/**
* 为指定节点创建变量
*/
private createVarForNode(node: any): void {
var moduleName: string = this.getPackageByNode(node);
if (moduleName == "")
return;
if (!this.currentClass.containsVar(node.$id))
this.currentClass.addVariable(new CpVariable(node.$id, Modifiers.M_PUBLIC, moduleName));
}
/**
* 为指定节点创建初始化函数,返回函数名引用
*/
private createFuncForNode(node: any): string {
var className: string = node.localName;
if (this.isProperty(node))
return "";
var isBasicType: boolean = this.isBasicTypeData(className);
if (isBasicType)
return this.createBasicTypeForNode(node);
var moduleName: string = this.getPackageByNode(node);
var func: CpFunction = new CpFunction;
var tailName: string = "_i";
var id: string = node.$id;
func.name = id + tailName;
func.returnType = moduleName;
var cb: CpCodeBlock = new CpCodeBlock;
var varName: string = "t";
if (className == "Object") {
cb.addVar(varName, "any", "{}");
}
else {
cb.addVar(varName, moduleName, "new " + moduleName + "()");
}
var containsId: boolean = this.currentClass.containsVar(node.$id);
if (containsId) {
cb.addAssignment("this." + node.$id, varName);
}
this.addAttributesToCodeBlock(cb, varName, node);
this.initlizeChildNode(node, cb, varName);
if (this.delayAssignmentDic[id]) {
var length = this.delayAssignmentDic[id].length;
for (var i: number = 0; i < length; i++) {
var delaycb: any = this.delayAssignmentDic[id][i];
cb.concat(delaycb);
}
}
cb.addReturn(varName);
func.codeBlock = cb;
this.currentClass.addFunction(func);
return "this." + func.name + "()";
}
private basicTypes: Array<string> = ["Array", "boolean", "string", "number"];
/**
* 检查目标类名是否是基本数据类型
*/
private isBasicTypeData(className: string): boolean {
return this.basicTypes.indexOf(className) != -1;
}
/**
* 为指定基本数据类型节点实例化,返回实例化后的值。
*/
private createBasicTypeForNode(node: any): string {
var className: string = node.localName;
var returnValue: string = "";
var child: any;
var varItem: CpVariable = this.currentClass.getVariableByName(node.$id);
switch (className) {
case "Array":
var values: Array<any> = [];
var children: Array<any> = node.children;
if (children) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var child: any = children[i];
values.push(this.createFuncForNode(child));
}
}
returnValue = "[" + values.join(",") + "]";
break;
case "boolean":
returnValue = node.text.trim();
returnValue = (returnValue == "false" || !returnValue) ? "false" : "true";
break;
case "number":
returnValue = node.text.trim();
if (returnValue.indexOf("%") != -1)
returnValue = returnValue.substring(0, returnValue.length - 1);
break;
case "string":
returnValue = this.formatString(node.toString());
break;
}
if (varItem)
varItem.defaultValue = returnValue;
return returnValue;
}
/**
* 延迟赋值字典
*/
private delayAssignmentDic: any = {};
/**
* 将节点属性赋值语句添加到代码块
*/
private addAttributesToCodeBlock(cb: CpCodeBlock, varName: string, node: any): void {
var keyList: Array<any> = [];
var key: string;
var value: string;
for (key in node) {
if (key.charAt(0) != "$" || !this.isNormalKey(key)) {
continue;
}
keyList.push(key);
}
keyList.sort();//排序一下防止出现随机顺序
var className: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
var length: number = keyList.length;
var values: Array<any> = [];
var keys: Array<any> = [];
for (var i: number = 0; i < length; i++) {
key = keyList[i];
value = node[key];
key = this.formatKey(key.substring(1), value);
value = this.formatValue(key, value, node);
if (!value) {
continue;
}
if (this.currentClass.containsVar(value)) {//赋的值对象是一个id
var id: string = node.$id;
var codeLine: string = "this." + id + " = t;";
if (!this.currentClass.containsVar(id))
this.createVarForNode(node);
if (!cb.containsCodeLine(codeLine)) {
cb.addCodeLineAt(codeLine, 1);
}
var delayCb: CpCodeBlock = new CpCodeBlock();
if (varName == KeyWords.KW_THIS) {
delayCb.addAssignment(varName, "this." + value, key);
}
else {
delayCb.startIf("this." + id);
delayCb.addAssignment("this." + id, "this." + value, key);
delayCb.endBlock();
}
if (!this.delayAssignmentDic[value]) {
this.delayAssignmentDic[value] = [];
}
this.delayAssignmentDic[value].push(delayCb);
value = "this." + value;
}
if (this.exmlConfig.isStyleProperty(key, className)) {
cb.addCodeLine(varName + ".setStyle(\"" + key + "\"," + value + ")");
}
else {
keys.push(key);
values.push(value);
}
}
var length: number = keys.length;
if (length > 1) {
var allKey: string = "[\"" + keys.join("\",\"") + "\"]";
var allValue: string = "[" + values.join(",") + "]"
cb.addCodeLine("this.__s(" + varName + "," + allKey + "," + allValue + ")");
}
else if (length == 1) {
cb.addAssignment(varName, values[0], keys[0]);
}
}
/**
* 初始化子项
*/
private initlizeChildNode(node: any, cb: CpCodeBlock, varName: string): void {
var children: Array<any> = node.children;
if (!children || children.length == 0)
return;
var className: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
var directChild: Array<any> = [];
var length: number = children.length;
var propList: Array<string> = [];
var isContainer: boolean = this.exmlConfig.isInstanceOf(className, "egret.gui.IContainer");
for (var i: number = 0; i < length; i++) {
var child: any = children[i];
var prop: string = child.localName;
if (child.namespace == EXMLCompiler.W) {
continue;
}
if (this.isProperty(child)) {
if (!this.isNormalKey(prop)) {
continue;
}
var type: string = this.exmlConfig.getPropertyType(child.localName, className);
if (!type) {
globals.warn(2005, this.exmlPath, child.localName, this.getPropertyStr(child));
}
if (!child.children || child.children.length == 0) {
globals.warn(2102, this.exmlPath, this.getPropertyStr(child));
continue;
}
var errorInfo: string = this.getPropertyStr(child);
this.addChildrenToProp(child.children, type, prop, cb, varName, errorInfo, propList, className, isContainer);
}
else {
directChild.push(child);
}
}
if (directChild.length == 0)
return;
var defaultProp: string = this.exmlConfig.getDefaultPropById(node.localName, node.namespace);
var defaultType: string = this.exmlConfig.getPropertyType(defaultProp, className);
var errorInfo: string = this.getPropertyStr(directChild[0]);
if (!defaultProp || !defaultType) {
this.exit(2012, this.exmlPath, errorInfo);
}
this.addChildrenToProp(directChild, defaultType, defaultProp, cb, varName, errorInfo, propList, className, isContainer);
}
/**
* 添加多个子节点到指定的属性
*/
private addChildrenToProp(children: Array<any>, type: string, prop: string,
cb: CpCodeBlock, varName: string, errorInfo: string,
propList: Array<string>, className: string,
isContainer: boolean): void {
var childFunc: string = "";
var childLength: number = children.length;
if (childLength > 1) {
if (type != "Array") {
this.exit(2011, this.exmlPath, prop, errorInfo)
}
var values: Array<any> = [];
for (var j: number = 0; j < childLength; j++) {
var item: any = children[j];
childFunc = this.createFuncForNode(item);
var childClassName: string = this.exmlConfig.getClassNameById(item.localName, item.namespace);
if (!this.isStateNode(item))
values.push(childFunc);
}
childFunc = "[" + values.join(",") + "]";
}
else {
var firstChild: any = children[0];
if (type == "Array") {
if (firstChild.localName == "Array") {
values = [];
if (firstChild.children) {
var len: number = firstChild.children.length;
for (var k: number = 0; k < len; k++) {
item = firstChild.children[k];
childFunc = this.createFuncForNode(item);
childClassName = this.exmlConfig.getClassNameById(item.localName, item.namespace);
if (!this.isStateNode(item))
values.push(childFunc);
}
}
childFunc = "[" + values.join(",") + "]";
}
else {
childFunc = this.createFuncForNode(firstChild);
var childClassName: string = this.exmlConfig.getClassNameById(firstChild.localName, firstChild.namespace);
if (!this.isStateNode(firstChild))
childFunc = "[" + childFunc + "]";
else
childFunc = "[]";
}
}
else {
var targetClass: string = this.exmlConfig.getClassNameById(firstChild.localName, firstChild.namespace);
if (!this.exmlConfig.isInstanceOf(targetClass, type)) {
this.exit(2008, this.exmlPath, targetClass, prop, errorInfo);
}
childFunc = this.createFuncForNode(firstChild);
}
}
if (childFunc != "") {
if (childFunc.indexOf("()") == -1)
prop = this.formatKey(prop, childFunc);
if (propList.indexOf(prop) == -1) {
propList.push(prop);
}
else {
globals.warn(2103, this.exmlPath, prop, errorInfo);
}
if (this.exmlConfig.isStyleProperty(prop, className)) {
cb.addCodeLine(varName + ".setStyle(\"" + prop + "\"," + childFunc + ")");
}
else {
cb.addAssignment(varName, childFunc, prop);
}
}
}
private getPropertyStr(child: any): string {
var parentStr: string = this.toXMLString(child.parent);
var childStr: string = this.toXMLString(child).substring(5);
return parentStr + "\n \t" + childStr;
}
/**
* 指定节点是否是属性节点
*/
private isProperty(node: any): boolean {
var name: string = node.localName;
if (name == null)
return true;
if (this.isBasicTypeData(name))
return false;
var firstChar: string = name.charAt(0);
return firstChar < "A" || firstChar > "Z";
}
/**
* 命名空间为fs的属性名列表
*/
public static wingKeys: Array<string> = ["$id", "$locked", "$includeIn", "$excludeFrom", "id", "locked", "includeIn", "excludeFrom"];
/**
* 是否是普通赋值的key
*/
private isNormalKey(key: string): boolean {
if (!key || key.indexOf(".") != -1 || EXMLCompiler.wingKeys.indexOf(key) != -1)
return false;
return true;
}
/**
* 格式化key
*/
private formatKey(key: string, value: string): string {
if (value.indexOf("%") != -1) {
if (key == "height")
key = "percentHeight";
else if (key == "width")
key = "percentWidth";
}
return key;
}
/**
* 格式化值
*/
private formatValue(key: string, value: string, node: any): string {
if (!value) {
value = "";
}
var stringValue: string = value;//除了字符串,其他类型都去除两端多余空格。
value = value.trim();
var className: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
var type: string = this.exmlConfig.getPropertyType(key, className);
if (!type) {
globals.warn(2005, this.exmlPath, key, this.toXMLString(node));
}
if (type != "string" && value.charAt(0) == "{" && value.charAt(value.length - 1) == "}") {
value = value.substr(1, value.length - 2);
value = value.trim();
if (value.indexOf("this.") == 0) {
if (CodeUtil.isVariableWord(value.substring(5))) {
value = value.substring(5);
}
}
if (CodeUtil.isVariableWord(value)) {
var targetNode: any = this.idToNode[value];
if (!targetNode) {
this.exit(2010, this.exmlPath, key, value, this.toXMLString(node));
}
var targetClass: string = this.exmlConfig.getClassNameById(targetNode.localName, targetNode.namespace);
if (!this.exmlConfig.isInstanceOf(targetClass, type)) {
this.exit(2008, this.exmlPath, targetClass, key, this.toXMLString(node));
}
}
else {
this.exit(2009, this.exmlPath, this.toXMLString(node));
}
}
else if (type == "Class" && value.indexOf("@ButtonSkin(") == 0 && value.charAt(value.length - 1) == ")") {
value = value.substring(12, value.length - 1);
var skinNames: Array<string> = value.split(",");
if (skinNames.length > 3) {
this.exit(2018, this.exmlPath, this.toXMLString(node));
}
for (var i: number = skinNames.length - 1; i >= 0; i--) {
var skinName: string = skinNames[i];
skinName = skinName.trim();
var firstChar: string = skinName.charAt(0);
var lastChar: string = skinName.charAt(skinName.length - 1);
if (firstChar != lastChar || (firstChar != "'" && firstChar != "\"")) {
this.exit(2018, this.exmlPath, this.toXMLString(node));
break;
}
skinNames[i] = this.formatString(skinName.substring(1, skinName.length - 1));
}
value = "new egret.gui.ButtonSkin(" + skinNames.join(",") + ")";
}
else if (key == "scale9Grid" && type == "egret.Rectangle") {
var rect: Array<any> = value.split(",");
if (rect.length != 4 || isNaN(parseInt(rect[0])) || isNaN(parseInt(rect[1])) ||
isNaN(parseInt(rect[2])) || isNaN(parseInt(rect[3]))) {
this.exit(2016, this.exmlPath, this.toXMLString(node));
}
value = "egret.gui.getScale9Grid(\"" + value + "\")";
}
else {
var orgValue: string = value;
switch (type) {
case "egret.gui.IFactory":
value = "new egret.gui.ClassFactory(" + orgValue + ")";
case "Class":
if (!this.exmlConfig.checkClassName(orgValue)) {
this.exit(2015, this.exmlPath, orgValue, this.toXMLString(node));
}
if (value == this.currentClassName) {//防止无限循环。
this.exit(2014, this.exmlPath, this.toXMLString(node));
}
break;
case "number":
if (value.indexOf("#") == 0)
value = "0x" + value.substring(1);
else if (value.indexOf("%") != -1)
value = (parseFloat(value.substr(0, value.length - 1))).toString();
break;
case "boolean":
value = (value == "false" || !value) ? "false" : "true";
break;
case "string":
case "any":
value = this.formatString(stringValue);
break;
default:
this.exit(2008, this.exmlPath, "string", key + ":" + type, this.toXMLString(node));
break;
}
}
return value;
}
/**
* 格式化字符串
*/
private formatString(value: string): string {
value = this.unescapeHTMLEntity(value);
value = value.split("\n").join("\\n");
value = value.split("\r").join("\\n");
value = value.split("\"").join("\\\"");
value = "\"" + value + "\"";
return value;
}
private htmlEntities: Array<any> = [["<", "<"], [">", ">"], ["&", "&"], ["\"", """], ["'", "'"]];
/**
/**
* 转换HTML实体字符为普通字符
*/
private unescapeHTMLEntity(str: string): string {
if (!str)
return "";
var list: Array<any> = this.htmlEntities;
var length: number = list.length;
for (var i: number = 0; i < length; i++) {
var arr: Array<any> = list[i];
var key: string = arr[0];
var value: string = arr[1];
str = str.split(value).join(key);
}
return str;
}
/**
* 创建构造函数
*/
private createConstructFunc(): void {
var cb: CpCodeBlock = new CpCodeBlock;
cb.addEmptyLine();
var varName: string = KeyWords.KW_THIS;
this.addAttributesToCodeBlock(cb, varName, this.currentXML);
if (this.declarations) {
var children: Array<any> = this.declarations.children;
if (children && children.length > 0) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var decl: any = children[i];
var funcName: string = this.createFuncForNode(decl);
if (funcName != "") {
cb.addCodeLine(funcName + ";");
}
}
}
}
this.initlizeChildNode(this.currentXML, cb, varName);
var id: string;
if (this.stateIds.length > 0) {
length = this.stateIds.length;
for (var i: number = 0; i < length; i++) {
id = this.stateIds[i];
cb.addCodeLine("this." + id + "_i();");
}
cb.addEmptyLine();
}
length = this.skinParts.length;
if (length > 0) {
for (i = 0; i < length; i++) {
this.skinParts[i] = "\"" + this.skinParts[i] + "\"";
}
var skinPartStr: string = "[" + this.skinParts.join(",") + "]";
var skinPartVar: CpVariable = new CpVariable("_skinParts", Modifiers.M_PRIVATE, "Array<string>", skinPartStr, true);
this.currentClass.addVariable(skinPartVar);
var skinPartFunc: CpFunction = new CpFunction();
skinPartFunc.name = "skinParts";
skinPartFunc.modifierName = Modifiers.M_PUBLIC;
skinPartFunc.isGet = true;
skinPartFunc.returnType = "Array<string>";
var skinPartCB: CpCodeBlock = new CpCodeBlock();
skinPartCB.addReturn(this.currentClass.className + "._skinParts");
skinPartFunc.codeBlock = skinPartCB;
this.currentClass.addFunction(skinPartFunc);
}
this.currentXML.$id = "";
//生成视图状态代码
this.createStates(this.currentXML);
var states: Array<CpState>;
var node: any = this.currentXML;
var nodeClassName: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
for (var itemName in node) {
var value: string = node[itemName];
if (itemName.charAt(0) != "$") {
continue;
}
itemName = itemName.substring(1);
var index: number = itemName.indexOf(".");
if (index != -1) {
var key: string = itemName.substring(0, index);
key = this.formatKey(key, value);
var itemValue: string = this.formatValue(key, value, node);
if (!itemValue) {
continue;
}
var stateName: string = itemName.substr(index + 1);
states = this.getStateByName(stateName, node);
var stateLength: number = states.length;
if (stateLength > 0) {
for (var i: number = 0; i < stateLength; i++) {
var state: CpState = states[i];
if (this.exmlConfig.isStyleProperty(key, nodeClassName)) {
state.addOverride(new CpSetStyle("", key, itemValue));
}
else {
state.addOverride(new CpSetProperty("", key, itemValue));
}
}
}
}
}
//打印视图状态初始化代码
if (this.stateCode.length > 0) {
cb.addCodeLine("this.states = [");
var first: boolean = true;
var indentStr: string = " ";
var length: number = this.stateCode.length;
for (var i: number = 0; i < length; i++) {
state = this.stateCode[i];
if (first)
first = false;
else
cb.addCodeLine(indentStr + ",");
var codes: Array<any> = state.toCode().split("\n");
var codeIndex: number = 0;
while (codeIndex < codes.length) {
var code: string = codes[codeIndex];
if (code)
cb.addCodeLine(indentStr + code);
codeIndex++;
}
}
cb.addCodeLine("];");
}
this.currentClass.constructCode = cb;
}
/**
* 是否含有includeIn和excludeFrom属性
*/
private isStateNode(node: any): boolean {
return node.hasOwnProperty("$includeIn") || node.hasOwnProperty("$excludeFrom");
}
/**
* 获取视图状态名称列表
*/
private getStateNames(): void {
var stateNames: Array<string> = this.stateNames;
var states: Array<any>;
var children = this.currentXML.children;
if (children) {
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var item: any = children[i];
if (item.localName == "states") {
item.namespace = EXMLCompiler.W;
states = item.children;
break;
}
}
}
if (states == null)
return;
if (states.length == 0) {
globals.warn(2102, this.exmlPath, this.getPropertyStr(item));
return;
}
length = states.length;
for (i = 0; i < length; i++) {
var state: any = states[i];
var stateGroups: Array<any> = [];
if (state["$stateGroups"]) {
var groups: Array<any> = state.$stateGroups.split(",");
var len: number = groups.length;
for (var j: number = 0; j < len; j++) {
var group: string = groups[j].trim();
if (group) {
if (stateNames.indexOf(group) == -1) {
stateNames.push(group);
}
stateGroups.push(group);
}
}
}
var stateName: string = state.$name;
if (stateNames.indexOf(stateName) == -1) {
stateNames.push(stateName);
}
this.stateCode.push(new CpState(stateName, stateGroups));
}
}
/**
* 解析视图状态代码
*/
private createStates(parentNode: any): void {
var items: Array<any> = parentNode.children;
if (!items) {
return;
}
var className: string = this.exmlConfig.getClassNameById(parentNode.localName, parentNode.namespace);
var length: number = items.length;
for (var i: number = 0; i < length; i++) {
var node: any = items[i];
this.createStates(node);
if (node.namespace == EXMLCompiler.W || !node.localName) {
continue;
}
if (this.isProperty(node)) {
var prop: string = node.localName;
var index: number = prop.indexOf(".");
var children: Array<any> = node.children;
if (index == -1 || !children || children.length == 0) {
continue;
}
var stateName: string = prop.substring(index + 1);
prop = prop.substring(0, index);
var type: string = this.exmlConfig.getPropertyType(prop, className);
if (type == "Array") {
this.exit(2013, this.exmlPath, this.getPropertyStr(node));
}
if (children.length > 1) {
this.exit(2011, this.exmlPath, prop, this.getPropertyStr(node));
}
var firstChild: any = children[0];
this.createFuncForNode(firstChild);
this.checkIdForState(firstChild);
var value: string = "this." + firstChild.$id;
states = this.getStateByName(stateName, node);
var l: number = states.length;
if (l > 0) {
for (var j: number = 0; j < l; j++) {
state = states[j];
if (this.exmlConfig.isStyleProperty(prop, className)) {
state.addOverride(new CpSetStyle(parentNode.$id, prop, value));
}
else {
state.addOverride(new CpSetProperty(parentNode.$id, prop, value));
}
}
}
}
else if (this.containsState(node)) {
var id: string = node.$id;
var nodeClassName: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
this.checkIdForState(node);
var stateName: string;
var states: Array<CpState>;
var state: CpState;
if (this.isStateNode(node)) {
if (!this.isIVisualElement(node)) {
this.exit(2007, this.exmlPath, this.toXMLString(node));
}
var propertyName: string = "";
var parent: any = (node.parent);
if (parent.localName == "Array")
parent = parent.parent;
if (this.isProperty(parent))
parent = parent.parent;
if (parent && parent != this.currentXML) {
propertyName = parent.$id;
this.checkIdForState(parent);
}
var positionObj: any = this.findNearNodeId(node);
var stateNames: Array<any> = [];
if (node.hasOwnProperty("$includeIn")) {
stateNames = node.$includeIn.split(",");
}
else {
var excludeNames: Array<any> = node.$excludeFrom.split(",");
var stateLength: number = excludeNames.length;
for (var j: number = 0; j < stateLength; j++) {
var name: string = excludeNames[j];
this.getStateByName(name, node);//检查exlcudeFrom是否含有未定义的视图状态名
}
stateLength = this.stateCode.length;
for (j = 0; j < stateLength; j++) {
state = this.stateCode[j];
if (excludeNames.indexOf(state.name) == -1)
stateNames.push(state.name);
}
}
var len: number = stateNames.length;
for (var k: number = 0; k < len; k++) {
stateName = stateNames[k];
states = this.getStateByName(stateName, node);
if (states.length > 0) {
var l: number = states.length;
for (var j: number = 0; j < l; j++) {
state = states[j];
state.addOverride(new CpAddItems(id, propertyName,
positionObj.position, positionObj.relativeTo));
}
}
}
}
var name: string;
for (name in node) {
var value: string = node[name];
if (name.charAt(0) != "$") {
continue;
}
name = name.substring(1);
var index: number = name.indexOf(".");
if (index != -1) {
var key: string = name.substring(0, index);
key = this.formatKey(key, value);
var value: string = this.formatValue(key, value, node);
if (!value) {
continue;
}
stateName = name.substr(index + 1);
states = this.getStateByName(stateName, node);
var l: number = states.length;
if (l > 0) {
for (var j: number = 0; j < l; j++) {
state = states[j];
if (this.exmlConfig.isStyleProperty(key, nodeClassName)) {
state.addOverride(new CpSetStyle(id, key, value));
}
else {
state.addOverride(new CpSetProperty(id, key, value));
}
}
}
}
}
}
}
}
/**
* 检查指定的节点是否是显示对象
*/
private isIVisualElement(node: any): boolean {
var className: string = this.exmlConfig.getClassNameById(node.localName, node.namespace);
var result: boolean = this.exmlConfig.isInstanceOf(className, "egret.gui.IVisualElement");
if (!result) {
return false;
}
var parent: any = node.parent;
if (!parent) {
return false;
}
if (parent.localName == "Array") {
parent = parent.parent;
}
if (!parent) {
return false;
}
if (this.isProperty(parent)) {
return (parent.localName == "elementsContent")
}
var prop: string = this.exmlConfig.getDefaultPropById(parent.localName, parent.namespace);
return prop == "elementsContent";
}
/**
* 检查指定的ID是否创建了类成员变量,若没创建则为其创建。
*/
private checkIdForState(node: any): void {
if (!node || this.currentClass.containsVar(node.$id)) {
return;
}
this.createVarForNode(node);
var id: string = node.$id;
var funcName: string = id + "_i";
var func: CpFunction = this.currentClass.getFuncByName(funcName);
if (!func)
return;
var codeLine: string = "this." + id + " = t;";
var cb: CpCodeBlock = func.codeBlock;
if (!cb)
return;
if (!cb.containsCodeLine(codeLine)) {
cb.addCodeLineAt(codeLine, 1);
}
}
/**
* 通过视图状态名称获取对应的视图状态
*/
private getStateByName(name: string, node: any): Array<CpState> {
var states: Array<CpState> = [];
var length: number = this.stateCode.length;
for (var i: number = 0; i < length; i++) {
var state: CpState = this.stateCode[i];
if (state.name == name) {
if (states.indexOf(state) == -1)
states.push(state);
}
else if (state.stateGroups.length > 0) {
var found: boolean = false;
var len: number = state.stateGroups.length;
for (var j: number = 0; j < len; j++) {
var g: string = state.stateGroups[j];
if (g == name) {
found = true;
break;
}
}
if (found) {
if (states.indexOf(state) == -1)
states.push(state);
}
}
}
if (states.length == 0) {
this.exit(2006, this.exmlPath, name, this.toXMLString(node));
}
return states;
}
/**
* 寻找节点的临近节点ID和位置
*/
private findNearNodeId(node: any): any {
var parentNode: any = node.parent;
var targetId: string = "";
var postion: string;
var index: number = -1;
var totalCount: number = 0;
var preItem: any;
var afterItem: any;
var found: boolean = false;
var children: Array<any> = parentNode.children;
var length: number = children.length;
for (var i: number = 0; i < length; i++) {
var item: any = children[i];
if (this.isProperty(item))
continue;
if (item == node) {
found = true;
index = i;
}
else {
if (found && !afterItem && !this.isStateNode(item)) {
afterItem = item;
}
}
if (!found && !this.isStateNode(item))
preItem = item;
}
if (index == 0) {
postion = "first";
return { position: postion, relativeTo: targetId };
}
if (index == length - 1) {
postion = "last";
return { position: postion, relativeTo: targetId };
}
if (afterItem) {
postion = "before";
targetId = afterItem.$id;
if (targetId) {
this.checkIdForState(afterItem);
return { position: postion, relativeTo: targetId };
}
}
return { position: "last", relativeTo: targetId };
}
/**
* 根据类名获取对应的包,并自动导入相应的包
*/
private getPackageByNode(node: any): string {
var moduleName: string =
this.exmlConfig.getClassNameById(node.localName, node.namespace);
if (!moduleName) {
this.exit(2003, this.exmlPath, this.toXMLString(node));
}
return moduleName;
}
/**
* 检查变量是否是包名
*/
private isPackageName(name: string): boolean {
return name.indexOf(".") != -1;
}
}
//=================代码生成工具类===================
class CodeBase {
public constructor() {
}
public toCode(): string {
return "";
}
public indent: number = 0;
/**
* 获取缩进字符串
*/
public getIndent(indent: number = -1): string {
if (indent == -1)
indent = this.indent;
var str: string = "";
for (var i: number = 0; i < indent; i++) {
str += " ";
}
return str;
}
}
class CpArguments extends CodeBase {
public constructor(name: string = "", type: string = "") {
super();
this.name = name;
this.type = type;
}
public name: string = "";
public type: string = "";
public toCode(): string {
return this.name + ":" + this.type;
}
}
class CpClass extends CodeBase {
public constructor() {
super();
this.indent = 1;
}
/**
* 构造函数的参数列表
*/
private argumentBlock: Array<any> = [];
/**
* 添加构造函数的参数
*/
public addArgument(argumentItem: CodeBase): void {
if (this.argumentBlock.indexOf(argumentItem) == -1) {
this.argumentBlock.push(argumentItem);
}
}
/**
* 构造函数代码块
*/
public constructCode: CpCodeBlock;
/**
* 类名
*/
public className: string = "CpClass";
/**
* 类所在的路径,用于计算reference的相对路径
*/
public classPath: string = "";
/**
* 包名
*/
public moduleName: string = "";
/**
* 父类类名
*/
public superClass: string = "";
/**
* 接口列表
*/
private interfaceBlock: Array<any> = [];
/**
* 添加接口
*/
public addInterface(interfaceName: string): void {
if (interfaceName == null || interfaceName == "")
return;
if (this.interfaceBlock.indexOf(interfaceName) == -1) {
this.interfaceBlock.push(interfaceName);
}
}
/**
* 引用文件区块
*/
private referenceBlock: Array<any> = [];
/**
* 引用一个文件
*/
public addReference(referenceItem: string): void {
if (referenceItem == null || referenceItem == "")
return;
if (this.referenceBlock.indexOf(referenceItem) == -1) {
this.referenceBlock.push(referenceItem);
}
}
/**
* 变量定义区块
*/
private variableBlock: Array<any> = [];
/**
* 添加变量
*/