UNPKG

dragonbones-runtime

Version:

the tools to build dragonbones file for diffrent framework

1,443 lines (1,349 loc) 72.4 kB
/// <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> = [["<", "&lt;"], [">", "&gt;"], ["&", "&amp;"], ["\"", "&quot;"], ["'", "&apos;"]]; /** /** * 转换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> = []; /** * 添加变量 */