dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
1,449 lines (1,362 loc) • 57.1 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014-present, Egret Technology.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Egret nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////
import { EXMLConfig, NS_S, NS_W } from "./EXMLConfig2";
import XMLParser = require("../xml/index");
import { EXAddItems, EXBinding, EXClass, EXCodeBlock, EXFunction, EXSetProperty, EXState, EXVariable, EXSetStateProperty } from "./CodeFactory";
let DEBUG = false;
import { egretbridge } from "./egretbridge";
/**
* @private
* EXML配置管理器实例
*/
export let exmlConfig: EXMLConfig;
let exmlParserPool: EXMLParser[] = [];
let parsedClasses: any = {};
let innerClassCount = 1;
let HOST_COMPONENT = "hostComponent";
let SKIN_CLASS = "eui.Skin";
let DECLARATIONS = "Declarations";
let RECTANGLE = "egret.Rectangle";
let TYPE_CLASS = "Class";
let TYPE_ARRAY = "Array";
let TYPE_PERCENTAGE = "Percentage";
let TYPE_STexture = "string | Texture";
let TYPE_STATE = "State[]";
let SKIN_NAME = "skinName";
let ELEMENTS_CONTENT = "elementsContent";
let basicTypes: string[] = [TYPE_ARRAY, TYPE_STexture, "boolean", "string", "number"];
let wingKeys: string[] = ["id", "locked", "includeIn", "excludeFrom"];
let htmlEntities: string[][] = [["<", "<"], [">", ">"], ["&", "&"], ["\"", """], ["'", "'"]];
let jsKeyWords: string[] = ["null", "NaN", "undefined", "true", "false"];
/**
* @private
*/
export class EXMLParser {
/**
* @private
*/
public constructor() {
if (DEBUG) {
this.repeatedIdMap = {};
this.getRepeatedIds = getRepeatedIds;
this.getIds = getIds;
this.checkDeclarations = checkDeclarations;
}
}
private _topNode: egretbridge.XML;
public get topNode(): egretbridge.XML {
return this._topNode;
}
private _className: string;
public get className(): string {
return this._className;
}
/**
* @private
* 获取重复的ID名
*/
public getRepeatedIds: (xml: egretbridge.XML) => string[];
/**
* @private
*/
private getIds: (xml: any, result: string[]) => void;
/**
* @private
*/
private repeatedIdMap: any;
/**
* @private
*/
private checkDeclarations: (declarations: egretbridge.XML, list: string[]) => void;
/**
* @private
* 当前类
*/
private currentClass: EXClass;
/**
* 当前exml的根节点是否为Skin
*/
private isSkinClass: boolean;
/**
* @private
* 当前编译的类名
*/
private currentClassName: string;
/**
* @private
* 当前要编译的EXML文件
*/
private currentXML: egretbridge.XML;
/**
* @private
* id缓存字典
*/
private idDic: any;
/**
* @private
* 状态代码列表
*/
private stateCode: EXState[];
/**
* @private
*/
private stateNames: string[];
/**
* @private
* 需要单独创建的实例id列表
*/
private stateIds: string[];
/**
* @private
*/
private skinParts: string[];
/**
* @private
*/
private bindings: EXBinding[];
/**
* @private
*/
private declarations: any;
/**
* @private
* 延迟赋值字典
*/
private delayAssignmentDic: any = {};
/**
* @private
* 编译指定的XML对象为JavaScript代码。
* @param xmlData 要编译的EXML文件内容
*
*/
public parse(text: string): {code:string, className:string} {
if (DEBUG) {
if (!text) {
egretbridge.$error(1003, "text");
}
}
let xmlData: any = null;
if (DEBUG) {
try {
xmlData = XMLParser.parse(text);
}
catch (e) {
egretbridge.$error(2002, text + "\n" + e.message);
}
} else {
xmlData = XMLParser.parse(text);
}
this._topNode = xmlData;
let hasClass: boolean = false;
let className: string = "";
if (xmlData.attributes["class"]) {
className = xmlData.attributes["class"];
delete xmlData.attributes["class"];
hasClass = !!className;
}
else {
className = "$exmlClass" + innerClassCount++;
}
this._className = className;
let exClass = this.parseClass(xmlData, className);
let code = exClass.toCode();
return {code, className};
}
/**
* @private
* 编译指定的XML对象为CpClass对象。
*/
private parseClass(xmlData: egretbridge.XML, className: string): EXClass {
if (!exmlConfig) {
exmlConfig = new EXMLConfig();
}
exmlConfig.dirPath = egret.args.projectDir;
this.currentXML = xmlData;
this.currentClassName = className;
this.delayAssignmentDic = {};
this.idDic = {};
this.stateCode = [];
this.stateNames = [];
this.skinParts = [];
this.bindings = [];
this.declarations = null;
this.currentClass = new EXClass();
this.stateIds = [];
let index = className.lastIndexOf(".");
if (index != -1) {
this.currentClass.className = className.substring(index + 1);
}
else {
this.currentClass.className = className;
}
this.startCompile();
let clazz = this.currentClass;
this.currentClass = null;
return clazz;
}
/**
* @private
* 开始编译
*/
private startCompile(): void {
if (DEBUG) {
let result = this.getRepeatedIds(this.currentXML);
if (result.length > 0) {
egretbridge.$error(2004, this.currentClassName, result.join("\n"));
}
}
let superClass = this.getClassNameOfNode(this.currentXML);
this.isSkinClass = (superClass == SKIN_CLASS);
this.currentClass.superClass = superClass;
this.getStateNames();
let children = this.currentXML.children;
if (children) {
let length = children.length;
for (let i = 0; i < length; i++) {
let node: any = children[i];
if (node.nodeType === 1 && node.namespace == NS_W &&
node.localName == DECLARATIONS) {
this.declarations = node;
break;
}
}
}
if (DEBUG) {
let list: string[] = [];
this.checkDeclarations(this.declarations, list);
if (list.length > 0) {
egretbridge.$error(2020, this.currentClassName, list.join("\n"));
}
}
if (!this.currentXML.namespace) {
if (DEBUG) {
egretbridge.$error(2017, this.currentClassName, toXMLString(this.currentXML));
}
return;
}
this.addIds(this.currentXML.children);
this.createConstructFunc();
}
/**
* @private
* 添加必须的id
*/
private addIds(items: any): void {
if (!items) {
return;
}
let length = items.length;
for (let i = 0; i < length; i++) {
let node: egretbridge.XML = items[i];
if (node.nodeType != 1) {
continue;
}
if (!node.namespace) {
if (DEBUG) {
egretbridge.$error(2017, this.currentClassName, toXMLString(node));
}
continue;
}
if (this.isInnerClass(node)) {
continue;
}
this.addIds(node.children);
if (node.namespace == NS_W || !node.localName) {
}
else if (this.isProperty(node)) {
let prop = node.localName;
let index = prop.indexOf(".");
let children: Array<any> = node.children;
if (index == -1 || !children || children.length == 0) {
continue;
}
let firstChild: egretbridge.XML = children[0];
this.stateIds.push(firstChild.attributes.id);
}
else if (node.nodeType === 1) {
let id = node.attributes["id"];
if (id) {
let e = new RegExp("^[a-zA-Z_$]{1}[a-z0-9A-Z_$]*");
if (id.match(e) == null) {
egretbridge.$warn(2022, id);
}
if(id.match(new RegExp(/ /g)) != null) {
egretbridge.$warn(2022, id);
}
if (this.skinParts.indexOf(id) == -1) {
this.skinParts.push(id);
}
this.createVarForNode(node);
if (this.isStateNode(node))//检查节点是否只存在于一个状态里,需要单独实例化
this.stateIds.push(id);
}
else {
this.createIdForNode(node);
if (this.isStateNode(node))
this.stateIds.push(node.attributes.id);
}
}
}
}
/**
* @private
* 是否为内部类。
*/
private isInnerClass(node: egretbridge.XML): boolean {
if (node.hasOwnProperty("isInnerClass")) {
return node["isInnerClass"];
}
let result = (node.localName == "Skin" && node.namespace == NS_S);
if (!result) {
if (this.isProperty(node)) {
result = false;
}
else {
let prop: string;
let parent = node.parent;
if (this.isProperty(parent)) {
prop = parent.localName;
let index = prop.indexOf(".");
if (index != -1) {
let stateName = prop.substring(index + 1);
prop = prop.substring(0, index);
}
parent = parent.parent;
}
else {
prop = exmlConfig.getDefaultPropById(parent.localName, parent.namespace);
}
let className = exmlConfig.getClassNameById(parent.localName, parent.namespace);
result = (exmlConfig.getPropertyType(prop, className) == TYPE_CLASS);
}
}
node["isInnerClass"] = result;
return result;
}
/**
* @private
* 检测指定节点的属性是否含有视图状态
*/
private containsState(node: egretbridge.XML): boolean {
let attributes = node.attributes;
if (attributes["includeIn"] || attributes["excludeFrom"]) {
return true;
}
let keys = Object.keys(attributes);
let length = keys.length;
for (let i = 0; i < length; i++) {
let name = keys[i];
if (name.indexOf(".") != -1) {
return true;
}
}
return false;
}
/**
* @private
* 为指定节点创建id属性
*/
private createIdForNode(node: egretbridge.XML): void {
let idName = this.getNodeId(node);
if (!this.idDic[idName])
this.idDic[idName] = 1;
else
this.idDic[idName]++;
idName += this.idDic[idName];
node.attributes.id = idName;
}
/**
* @private
* 获取节点ID
*/
private getNodeId(node: egretbridge.XML): string {
if (node.attributes["id"])
return node.attributes.id;
return "_" + node.localName;
}
/**
* @private
* 为指定节点创建变量
*/
private createVarForNode(node: egretbridge.XML): void {
let moduleName = this.getClassNameOfNode(node);
if (moduleName == "")
return;
if (!this.currentClass.getVariableByName(node.attributes.id))
this.currentClass.addVariable(new EXVariable(node.attributes.id));
}
/**
* @private
* 为指定节点创建初始化函数,返回函数名引用
*/
private createFuncForNode(node: egretbridge.XML): string {
let className = node.localName;
let isBasicType = this.isBasicTypeData(className);
if (isBasicType)
return this.createBasicTypeForNode(node);
let moduleName = this.getClassNameOfNode(node);
let func = new EXFunction();
let tailName = "_i";
let id = node.attributes.id;
func.name = id + tailName;
this.currentClass.addFunction(func);
let cb = new EXCodeBlock();
func.codeBlock = cb;
let varName: string = "t";
if (className == "Object") {
cb.addVar(varName, "{}");
}
else {
cb.addVar(varName, "new " + moduleName + "()");
}
let containsId = !!this.currentClass.getVariableByName(id);
if (containsId) {
cb.addAssignment("this." + id, varName);
}
this.addAttributesToCodeBlock(cb, varName, node);
this.initlizeChildNode(node, cb, varName);
let delayAssignments = this.delayAssignmentDic[id];
if (delayAssignments) {
let length = delayAssignments.length;
for (let i = 0; i < length; i++) {
let codeBlock: EXCodeBlock = delayAssignments[i];
cb.concat(codeBlock);
}
}
cb.addReturn(varName);
return "this." + func.name + "()";
}
/**
* @private
* 检查目标类名是否是基本数据类型
*/
private isBasicTypeData(className: string): boolean {
return basicTypes.indexOf(className) != -1;
}
/**
* @private
* 为指定基本数据类型节点实例化,返回实例化后的值。
*/
private createBasicTypeForNode(node: egretbridge.XML): string {
let className = node.localName;
let returnValue = "";
let varItem = this.currentClass.getVariableByName(node.attributes.id);
let children: any[] = node.children;
let text = "";
if (children && children.length > 0) {
let firstChild: egretbridge.XMLText = children[0];
if (firstChild.nodeType == 3) {
text = firstChild.text.trim();
}
}
switch (className) {
case TYPE_ARRAY:
let values = [];
if (children) {
let length = children.length;
for (let i = 0; i < length; i++) {
let child: egretbridge.XML = children[i];
if (child.nodeType == 1) {
values.push(this.createFuncForNode(child));
}
}
}
returnValue = "[" + values.join(",") + "]";
break;
case "boolean":
returnValue = (text == "false" || !text) ? "false" : "true";
break;
case "number":
returnValue = text;
if (returnValue.indexOf("%") != -1)
returnValue = returnValue.substring(0, returnValue.length - 1);
break;
case "string":
returnValue = this.formatString(text);
break;
}
if (varItem)
varItem.defaultValue = returnValue;
return returnValue;
}
/**
* @private
* 将节点属性赋值语句添加到代码块
*/
private addAttributesToCodeBlock(cb: EXCodeBlock, varName: string, node: egretbridge.XML): void {
let key: string;
let value: string;
let attributes = node.attributes;
let keyList: string[] = Object.keys(attributes);
keyList.sort();//排序一下防止出现随机顺序
//对 style 属性先行赋值
let styleIndex = keyList.indexOf("style");
if (styleIndex > 0) {
keyList.splice(styleIndex, 1);
keyList.unshift("style");
}
let length = keyList.length;
for (let i = 0; i < length; i++) {
key = keyList[i];
if (!this.isNormalKey(key)) {
continue;
}
value = attributes[key];
key = this.formatKey(key, value);
value = this.formatValue(key, value, node);
if (!value) {
continue;
}
if (this.currentClass.getVariableByName(value)) {//赋的值对象是一个id
let THIS = "this.";
let id = attributes.id;
let codeLine = THIS + id + " = t;";
if (!this.currentClass.getVariableByName(id))
this.createVarForNode(node);
if (!cb.containsCodeLine(codeLine)) {
cb.addCodeLineAt(codeLine, 1);
}
let delayCb = new EXCodeBlock();
if (varName == "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;
}
cb.addAssignment(varName, value, key);
}
}
/**
* @private
* 初始化子项
*/
private initlizeChildNode(node: egretbridge.XML, cb: EXCodeBlock, varName: string): void {
let children: Array<any> = node.children;
if (!children || children.length == 0)
return;
let className = exmlConfig.getClassNameById(node.localName, node.namespace);
let directChild: egretbridge.XML[] = [];
let length = children.length;
let propList: string[] = [];
let errorInfo: any;
for (let i = 0; i < length; i++) {
let child: egretbridge.XML = children[i];
if (child.nodeType != 1 || child.namespace == NS_W) {
continue;
}
if (this.isInnerClass(child)) {
if (child.localName == "Skin") {
let innerClassName = this.parseInnerClass(child);
let type = exmlConfig.getPropertyType(SKIN_NAME, className);
if (type) {
cb.addAssignment(varName, innerClassName, SKIN_NAME);
}
else {
egretbridge.$error(2005, this.currentClassName, SKIN_NAME, getPropertyStr(child));
}
}
continue;
}
let prop = child.localName;
if (this.isProperty(child)) {
if (!this.isNormalKey(prop)) {
continue;
}
let type = exmlConfig.getPropertyType(child.localName, className);
if (!type) {
if (DEBUG) {
egretbridge.$error(2005, this.currentClassName, child.localName, getPropertyStr(child));
}
continue;
}
if (!child.children || child.children.length == 0) {
if (DEBUG) {
egretbridge.$warn(2102, this.currentClassName, getPropertyStr(child));
}
continue;
}
if (DEBUG) {
let errorInfo = getPropertyStr(child);
}
this.addChildrenToProp(child.children, type, prop, cb, varName, errorInfo, propList, node);
}
else {
directChild.push(child);
}
}
if (directChild.length == 0)
return;
let defaultProp = exmlConfig.getDefaultPropById(node.localName, node.namespace);
let defaultType = exmlConfig.getPropertyType(defaultProp, className);
if (DEBUG) {
errorInfo = getPropertyStr(directChild[0]);
}
if (!defaultProp || !defaultType) {
if (DEBUG) {
egretbridge.$error(2012, this.currentClassName, errorInfo);
}
return;
}
this.addChildrenToProp(directChild, defaultType, defaultProp, cb, varName, errorInfo, propList, node);
}
/**
* @private
* 解析内部类节点,并返回类名。
*/
private parseInnerClass(node: egretbridge.XML): string {
let parser = exmlParserPool.pop();
if (!parser) {
parser = new EXMLParser();
}
let innerClassName = this.currentClass.className + "$" + node.localName + innerClassCount++;
let innerClass = parser.parseClass(node, innerClassName);
this.currentClass.addInnerClass(innerClass);
exmlParserPool.push(parser);
return innerClassName;
}
/**
* @private
* 添加多个子节点到指定的属性
*/
private addChildrenToProp(children: Array<any>, type: string, prop: string,
cb: EXCodeBlock, varName: string, errorInfo: string,
propList: string[], node: egretbridge.XML): void {
let childFunc = "";
let childLength = children.length;
if (childLength > 1) {
if (type != TYPE_ARRAY) {
if (DEBUG) {
egretbridge.$error(2011, this.currentClassName, prop, errorInfo);
}
return;
}
let values: string[] = [];
for (let j = 0; j < childLength; j++) {
let item: egretbridge.XML = children[j];
if (item.nodeType != 1) {
continue;
}
childFunc = this.createFuncForNode(item);
let childClassName = this.getClassNameOfNode(item);
if (!this.isStateNode(item))
values.push(childFunc);
}
childFunc = "[" + values.join(",") + "]";
}
else {
let firstChild: egretbridge.XML = children[0];
if (type == TYPE_ARRAY) {
if (firstChild.localName == TYPE_ARRAY) {
let values = [];
if (firstChild.children) {
let len = firstChild.children.length;
for (let k = 0; k < len; k++) {
let item = <any>firstChild.children[k];
if (item.nodeType != 1) {
continue;
}
childFunc = this.createFuncForNode(item);
let childClassName = this.getClassNameOfNode(item);
if (!this.isStateNode(item))
values.push(childFunc);
}
}
childFunc = "[" + values.join(",") + "]";
}
else {
childFunc = this.createFuncForNode(firstChild);
let childClassName = this.getClassNameOfNode(firstChild);
if (!this.isStateNode(firstChild))
childFunc = "[" + childFunc + "]";
else
childFunc = "[]";
}
}
else if (firstChild.nodeType == 1) {
if (type == TYPE_CLASS) {
if (childLength > 1) {
if (DEBUG) {
egretbridge.$error(2011, this.currentClassName, prop, errorInfo);
}
return;
}
childFunc = this.parseInnerClass(children[0]);
}
else {
let targetClass = this.getClassNameOfNode(firstChild);
childFunc = this.createFuncForNode(firstChild);
}
}
else {
childFunc = this.formatValue(prop, (<egretbridge.XMLText><any>firstChild).text, node);
}
}
if (childFunc != "") {
if (childFunc.indexOf("()") == -1)
prop = this.formatKey(prop, childFunc);
if (propList.indexOf(prop) == -1) {
propList.push(prop);
}
else if (DEBUG) {
egretbridge.$warn(2103, this.currentClassName, prop, errorInfo);
}
cb.addAssignment(varName, childFunc, prop);
}
}
/**
* @private
* 指定节点是否是属性节点
*/
private isProperty(node: egretbridge.XML): boolean {
if (node.hasOwnProperty("isProperty")) {
return node["isProperty"];
}
let result: boolean;
let name = node.localName;
if (!name || node.nodeType !== 1 || !node.parent || this.isBasicTypeData(name)) {
result = false;
}
else {
let parent = node.parent;
let index = name.indexOf(".")
if (index != -1) {
name = name.substr(0, index);
}
let className = exmlConfig.getClassNameById(parent.localName, parent.namespace);
result = !!exmlConfig.getPropertyType(name, className);
}
node["isProperty"] = result;
return result;
}
/**
* @private
* 是否是普通赋值的key
*/
private isNormalKey(key: string): boolean {
if (!key || key.indexOf(".") != -1
|| key.indexOf(":") != -1 || wingKeys.indexOf(key) != -1)
return false;
return true;
}
/**
* @private
* 格式化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
* 格式化值
*/
private formatValue(key: string, value: string, node: egretbridge.XML): string {
if (!value) {
value = "";
}
let stringValue = value;//除了字符串,其他类型都去除两端多余空格。
value = value.trim();
let className = this.getClassNameOfNode(node);
let type: string = exmlConfig.getPropertyType(key, className);
if (DEBUG && !type) {
egretbridge.$error(2005, this.currentClassName, key, toXMLString(node));
}
let bindingValue = this.formatBinding(key, value, node);
if (bindingValue) {
this.checkIdForState(node);
let target = "this";
if (node !== this.currentXML) {
target += "." + node.attributes["id"];
}
this.bindings.push(new EXBinding(target, key, bindingValue.templates, bindingValue.chainIndex));
value = "";
}
else if (type == RECTANGLE) {
if (DEBUG) {
let rect = value.split(",");
if (rect.length != 4 || isNaN(parseInt(rect[0])) || isNaN(parseInt(rect[1])) ||
isNaN(parseInt(rect[2])) || isNaN(parseInt(rect[3]))) {
egretbridge.$error(2016, this.currentClassName, toXMLString(node));
}
}
value = "new " + RECTANGLE + "(" + value + ")";
}
else if (type == TYPE_PERCENTAGE) {
if (value.indexOf("%") != -1) {
value = this.formatString(value);;
}
}
else {
let orgValue: string = value;
switch (type) {
case TYPE_CLASS:
if (key == SKIN_NAME) {
value = this.formatString(stringValue);
}
break;
case "number":
if (value.indexOf("#") == 0) {
if (DEBUG && isNaN(<any>value.substring(1))) {
egretbridge.$warn(2021, this.currentClassName, key, value);
}
value = "0x" + value.substring(1);
}
else if (value.indexOf("%") != -1) {
if (DEBUG && isNaN(<any>value.substr(0, value.length - 1))) {
egretbridge.$warn(2021, this.currentClassName, key, value);
}
value = (parseFloat(value.substr(0, value.length - 1))).toString();
}
else if (DEBUG && isNaN(<any>value)) {
egretbridge.$warn(2021, this.currentClassName, key, value);
}
break;
case "boolean":
value = (value == "false" || !value) ? "false" : "true";
break;
case "string":
case TYPE_STexture:
case "any":
value = this.formatString(stringValue);
break;
default:
if (DEBUG) {
egretbridge.$error(2008, this.currentClassName, "string", key + ":" + type, toXMLString(node));
}
break;
}
}
return value;
}
/**
* @private
* 格式化字符串
*/
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 formatBinding(key: string, value: string, node: egretbridge.XML): { templates: string[], chainIndex: number[] } {
if (!value) {
return null;
}
value = value.trim();
if (value.charAt(0) != "{" || value.charAt(value.length - 1) != "}") {
return null;
}
value = value.substring(1, value.length - 1).trim();
let templates = value.indexOf("+") == -1 ? [value] : this.parseTemplates(value);
let chainIndex: number[] = [];
let length = templates.length;
for (let i = 0; i < length; i++) {
let item = templates[i].trim();
if (!item) {
templates.splice(i, 1);
i--;
length--;
continue;
}
let first = item.charAt(0);
if (first == "'" || first == "\"" || first >= "0" && first <= "9" || first == "-") {
continue;
}
if (item.indexOf(".") == -1 && jsKeyWords.indexOf(item) != -1) {
continue;
}
if (item.indexOf("this.") == 0) {
item = item.substring(5);
}
let firstKey = item.split(".")[0];
if (firstKey != HOST_COMPONENT && this.skinParts.indexOf(firstKey) == -1) {
item = HOST_COMPONENT + "." + item;
}
templates[i] = "\"" + item + "\"";
chainIndex.push(i);
}
return { templates: templates, chainIndex: chainIndex };
}
private parseTemplates(value: string): string[] {
//仅仅是表达式相加 如:{a.b+c.d}
if (value.indexOf("'") == -1) {
return value.split("+");
}
//包含文本的需要提取文本并对文本进行处理
let isSingleQuoteLeak = false;//是否缺失单引号
let trimText = "";
value = value.split("\\\'").join("\v0\v");
while (value.length > 0) {
//'成对出现 这是第一个
let index = value.indexOf("'");
if (index == -1) {
trimText += value;
break;
}
trimText += value.substring(0, index + 1);
value = value.substring(index + 1);
//'成对出现 这是第二个
index = value.indexOf("'");
if (index == -1) {
index = value.length - 1;
isSingleQuoteLeak = true;
}
let quote = value.substring(0, index + 1);
trimText += quote.split("+").join("\v1\v");
value = value.substring(index + 1);
}
value = trimText.split("\v0\v").join("\\\'");
//补全缺失的单引号
if (isSingleQuoteLeak) {
value += "'";
}
let templates = value.split("+");
let length = templates.length;
for (let i = 0; i < length; i++) {
templates[i] = templates[i].split("\v1\v").join("+");
}
return templates;
}
/**
* @private
/**
* 转换HTML实体字符为普通字符
*/
private unescapeHTMLEntity(str: string): string {
if (!str)
return "";
let length = htmlEntities.length;
for (let i: number = 0; i < length; i++) {
let arr = htmlEntities[i];
let key: string = arr[0];
let value: string = arr[1];
str = str.split(value).join(key);
}
return str;
}
/**
* @private
* 创建构造函数
*/
private createConstructFunc(): void {
let cb: EXCodeBlock = new EXCodeBlock;
cb.addEmptyLine();
let varName: string = "this";
this.addAttributesToCodeBlock(cb, varName, this.currentXML);
if (this.declarations) {
let children: Array<any> = this.declarations.children;
if (children && children.length > 0) {
let length = children.length;
for (let i = 0; i < length; i++) {
let decl: egretbridge.XML = children[i];
if (decl.nodeType != 1) {
continue;
}
let funcName = this.createFuncForNode(decl);
if (funcName) {
cb.addCodeLine(funcName + ";");
}
}
}
}
this.initlizeChildNode(this.currentXML, cb, varName);
let id: string;
let stateIds = this.stateIds;
if (stateIds.length > 0) {
let length = stateIds.length;
for (let i = 0; i < length; i++) {
id = stateIds[i];
cb.addCodeLine("this." + id + "_i();");
}
cb.addEmptyLine();
}
let skinParts = this.skinParts;
let skinPartStr: string = "[]";
let length = skinParts.length;
if (length > 0) {
for (let i = 0; i < length; i++) {
skinParts[i] = "\"" + skinParts[i] + "\"";
}
skinPartStr = "[" + skinParts.join(",") + "]";
}
let skinPartFunc: EXFunction = new EXFunction();
skinPartFunc.name = "skinParts";
skinPartFunc.isGet = true;
let skinPartCB: EXCodeBlock = new EXCodeBlock();
skinPartCB.addReturn(skinPartStr);
skinPartFunc.codeBlock = skinPartCB;
this.currentClass.addFunction(skinPartFunc);
this.currentXML.attributes.id = "";
//生成视图状态代码
this.createStates(this.currentXML);
let states: EXState[];
let node = this.currentXML;
let nodeClassName = this.getClassNameOfNode(node);
let attributes = node.attributes;
let keys = Object.keys(attributes);
let keysLength = keys.length;
for (let m = 0; m < keysLength; m++) {
let itemName = keys[m];
let value: string = attributes[itemName];
let index = itemName.indexOf(".");
if (index != -1) {
let key = itemName.substring(0, index);
key = this.formatKey(key, value);
let itemValue = this.formatValue(key, value, node);
if (!itemValue) {
continue;
}
let stateName = itemName.substr(index + 1);
states = this.getStateByName(stateName, node);
let stateLength = states.length;
if (stateLength > 0) {
for (let i = 0; i < stateLength; i++) {
let state = states[i];
state.addOverride(new EXSetProperty("", key, itemValue));
}
}
}
}
//打印视图状态初始化代码
let stateCode = this.stateCode;
length = stateCode.length;
if (length > 0) {
let indentStr = " ";
cb.addCodeLine("this.states = [");
let first = true;
for (let i = 0; i < length; i++) {
let state = stateCode[i];
if (first)
first = false;
else
cb.addCodeLine(indentStr + ",");
let codes = state.toCode().split("\n");
let codeIndex = 0;
while (codeIndex < codes.length) {
let code = codes[codeIndex];
if (code)
cb.addCodeLine(indentStr + code);
codeIndex++;
}
}
cb.addCodeLine("];");
}
//生成绑定代码
let bindings = this.bindings;
length = bindings.length;
if (length > 0) {
cb.addEmptyLine();
for (let i = 0; i < length; i++) {
let binding = bindings[i];
cb.addCodeLine(binding.toCode());
}
}
this.currentClass.constructCode = cb;
}
/**
* @private
* 是否含有includeIn和excludeFrom属性
*/
private isStateNode(node: egretbridge.XML): boolean {
let attributes = node.attributes;
return attributes.hasOwnProperty("includeIn") || attributes.hasOwnProperty("excludeFrom");
}
/**
* @private
* 获取视图状态名称列表
*/
private getStateNames(): void {
let root = this.currentXML;
let className = exmlConfig.getClassNameById(root.localName, root.namespace);
let type = exmlConfig.getPropertyType("states", className);
if (type != TYPE_STATE) {
return;
}
let statesValue = root.attributes["states"];
if (statesValue) {
delete root.attributes["states"];
}
let stateNames = this.stateNames;
let stateChildren: any[];
let children: any[] = root.children;
let item: egretbridge.XML;
if (children) {
let length = children.length;
for (let i = 0; i < length; i++) {
item = children[i];
if (item.nodeType == 1 &&
item.localName == "states") {
item.namespace = NS_W;
stateChildren = item.children;
break;
}
}
}
if (!stateChildren && !statesValue) {
return;
}
if (DEBUG) {
if (stateChildren && stateChildren.length == 0) {
egretbridge.$warn(2102, this.currentClassName, getPropertyStr(item));
}
if (stateChildren && statesValue) {
egretbridge.$warn(2103, this.currentClassName, "states", getPropertyStr(item));
}
}
if (statesValue) {
let states = statesValue.split(",");
let length = states.length;
for (let i = 0; i < length; i++) {
let stateName: string = states[i].trim();
if (!stateName) {
continue;
}
if (stateNames.indexOf(stateName) == -1) {
stateNames.push(stateName);
}
this.stateCode.push(new EXState(stateName));
}
return;
}
let length = stateChildren.length;
for (let i = 0; i < length; i++) {
let state: egretbridge.XML = stateChildren[i];
if (state.nodeType != 1) {
continue;
}
let stateGroups: Array<any> = [];
let attributes = state.attributes;
if (attributes["stateGroups"]) {
let groups = attributes.stateGroups.split(",");
let len = groups.length;
for (let j = 0; j < len; j++) {
let group = groups[j].trim();
if (group) {
if (stateNames.indexOf(group) == -1) {
stateNames.push(group);
}
stateGroups.push(group);
}
}
}
let stateName = attributes.name;
if (stateNames.indexOf(stateName) == -1) {
stateNames.push(stateName);
}
this.stateCode.push(new EXState(stateName, stateGroups));
}
}
/**
* @private
* 解析视图状态代码
*/
private createStates(parentNode: egretbridge.XML): void {
let items: Array<any> = parentNode.children;
if (!items) {
return;
}
let length = items.length;
for (let i = 0; i < length; i++) {
let node: egretbridge.XML = items[i];
if (node.nodeType != 1 || this.isInnerClass(node)) {
continue;
}
this.createStates(node);
if (node.namespace == NS_W || !node.localName) {
continue;
}
if (this.isProperty(node)) {
let prop = node.localName;
let index = prop.indexOf(".");
let children: Array<any> = node.children;
if (index == -1 || !children || children.length == 0) {
continue;
}
let stateName = prop.substring(index + 1);
prop = prop.substring(0, index);
let className = this.getClassNameOfNode(parentNode);
let type = exmlConfig.getPropertyType(prop, className);
if (DEBUG) {
if (type == TYPE_ARRAY) {
egretbridge.$error(2013, this.currentClassName, getPropertyStr(node));
}
if (children.length > 1) {
egretbridge.$error(2011, this.currentClassName, prop, getPropertyStr(node));
}
}
let firstChild: egretbridge.XML = children[0];
let value: string;
if (firstChild.nodeType == 1) {
this.createFuncForNode(firstChild);
this.checkIdForState(firstChild);
value = "this." + firstChild.attributes.id;
}
else {
value = this.formatValue(prop, (<egretbridge.XMLText><any>firstChild).text, parentNode);
}
let states = this.getStateByName(stateName, node);
let l = states.length;
if (l > 0) {
for (let j: number = 0; j < l; j++) {
let state = states[j];
state.addOverride(new EXSetProperty(parentNode.attributes.id, prop, value));
}
}
}
else if (this.containsState(node)) {
let attributes = node.attributes;
let id = attributes.id;
let nodeClassName = this.getClassNameOfNode(node);
this.checkIdForState(node);
let stateName: string;
let states: Array<EXState>;
let state: EXState;
if (this.isStateNode(node)) {
let propertyName = "";
let parent: egretbridge.XML = node.parent;
if (parent.localName == TYPE_ARRAY)
parent = parent.parent;
if (parent && parent.parent) {
if (this.isProperty(parent))
parent = parent.parent;
}
if (parent && parent != this.currentXML) {
propertyName = parent.attributes.id;
this.checkIdForState(parent);
}
let positionObj = this.findNearNodeId(node);
let stateNames: string[] = [];
if (attributes.includeIn) {
stateNames = attributes.includeIn.split(",");
}
else {
let excludeNames = attributes.excludeFrom.split(",");
let stateLength = excludeNames.length;
for (let j = 0; j < stateLength; j++) {
let name: string = excludeNames[j];
this.getStateByName(name, node);//检查exlcudeFrom是否含有未定义的视图状态名
}
stateLength = this.stateCode.length;
for (let j = 0; j < stateLength; j++) {
state = this.stateCode[j];
if (excludeNames.indexOf(state.name) == -1) {
stateNames.push(state.name);
}
}
}
let len = stateNames.length;
for (let k = 0; k < len; k++) {
stateName = stateNames[k];
states = this.getStateByName(stateName, node);
if (states.length > 0) {
let l = states.length;
for (let j = 0; j < l; j++) {
state = states[j];
state.addOverride(new EXAddItems(id, propertyName,
positionObj.position, positionObj.relativeTo));
}
}
}
}
let names = Object.keys(attributes);
let namesLength = names.length;
for (let m = 0; m < namesLength; m++) {
let name = names[m];
let value: string = attributes[name];
let index: number = name.indexOf(".");
if (index != -1) {
let key = name.substring(0, index);
key = this.formatKey(key, value);
let bindingValue = this.formatBinding(key, value, node);
if (!bindingValue) {
value = this.formatValue(key, value, node);
if (!value) {
continue;
}
}
stateName = name.substr(index + 1);
states = this.getStateByName(stateName, node);
let l = states.length;
if (l > 0) {
for (let j = 0; j < l; j++) {
state = states[j];
if (bindingValue) {
state.addOverride(new EXSetStateProperty(id, key, bindingValue.templates, bindingValue.chainIndex));
} else {
state.addOverride(new EXSetProperty(id, key, value));
}
}
}
}
}
}
}
}
/**
* @private
* 检查指定的ID是否创建了类成员变量,若没创建则为其创建。
*/
private checkIdForState(node: egretbridge.XML): void {
if (!node || this.currentClass.getVariableByName(node.attributes.id)) {
return;
}
this.createVarForNode(node);
let id: string = node.attributes.id;
let funcName = id + "_i";
let func = this.currentClass.getFuncByName(funcName);
if (!func)
return;
let codeLine = "this." + id + " = t;";
let cb: EXCodeBlock = func.codeBlock;
if (!cb)
return;
if (!cb.containsCodeLine(codeLine)) {
cb.addCodeLineAt(codeLine, 1);
}
}
/**
* @private
* 通过视图状态名称获取对应的视图状态
*/
private getStateByName(name: string, node: egretbridge.XML): EXState[] {