marko/lib/compiler/TaglibXmlLoader.js
2014-01-07 16:59:30 -07:00

581 lines
25 KiB
JavaScript

/*
* Copyright 2011 eBay Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
define.Class(
"raptor/templating/compiler/TaglibXmlLoader",
['raptor'],
function(raptor, require, exports, module) {
"use strict";
var objectMapper = require('raptor/xml/sax/object-mapper'),
logger = module.logger(),
regexp = require('raptor/regexp'),
Taglib = require('raptor/templating/compiler/Taglib'),
Tag = Taglib.Tag,
Attribute = Taglib.Attribute,
Property = Taglib.Property,
NestedVariable = Taglib.NestedVariable,
ImportedVariable = Taglib.ImportedVariable,
Transformer = Taglib.Transformer,
Function = Taglib.Function,
HelperObject = Taglib.HelperObject,
STRING = "string",
BOOLEAN = "boolean",
OBJECT = "object";
var TaglibXmlLoader = function(src, resource) {
this.src = src;
this.filePath = typeof resource === 'string' ? resource : resource.getURL();
this.resource = resource;
};
TaglibXmlLoader.load = function(src, resource) {
var loader = new TaglibXmlLoader(src, resource);
return loader.load();
};
TaglibXmlLoader.prototype = {
load: function() {
var src = this.src,
filePath = this.filePath,
taglibResource = this.resource,
tagsById = {},
handleTagExtends = function(subTag) {
var extendsId = subTag['extends'];
if (!extendsId) {
return;
}
delete subTag['extends'];
var superTag = tagsById[extendsId];
if (!superTag) {
throw raptor.createError(new Error('Parent tag with ID "' + extendsId + '" not found in taglib at path "' + filePath + '"'));
}
if (superTag['extends']) {
handleTagExtends(superTag);
}
subTag.inheritFrom(superTag);
};
var taglib;
var attributeHandler = {
_type: OBJECT,
_begin: function() {
return new Attribute();
},
_end: function(attr, parent) {
if (attr.uri == null) {
attr.uri = '';
}
parent.addAttribute(attr);
},
"name": {
_type: STRING
},
"pattern": {
_type: STRING,
_set: function(parent, name, value) {
var patternRegExp = regexp.simple(value);
parent.pattern = patternRegExp;
}
},
"target-property": {
_type: STRING,
_targetProp: "targetProperty"
},
"uri": {
_type: STRING
},
"deprecated": {
_type: STRING
},
"required": {
_type: BOOLEAN
},
"type": {
_type: STRING
},
"allow-expressions": {
_type: BOOLEAN,
_targetProp: "allowExpressions"
},
"preserve-name": {
_type: BOOLEAN,
_targetProp: "preserveName"
},
"description": {
_type: STRING
}
};
var importVariableHandler = {
_type: OBJECT,
_begin: function() {
return new ImportedVariable();
},
_end: function(importedVariable, tag) {
if (importedVariable.name) {
if (!importedVariable.targetProperty) {
importedVariable.targetProperty = importedVariable.name;
}
importedVariable.expression = importedVariable.name;
delete importedVariable.name;
}
if (!importedVariable.targetProperty) {
throw raptor.createError(new Error('The "target-property" attribute is required for an imported variable'));
}
if (!importedVariable.expression) {
throw raptor.createError(new Error('The "expression" attribute is required for an imported variable'));
}
tag.addImportedVariable(importedVariable);
},
"name": { // Short-hand for target-property="<name>" and expression="<name>"
_type: STRING
},
"target-property": {
_type: STRING,
_targetProp: "targetProperty"
},
"expression": {
_type: STRING
}
};
var variableHandler = {
_type: OBJECT,
_begin: function() {
return new NestedVariable();
},
_end: function(nestedVariable, tag) {
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
throw raptor.createError(new Error('The "name" or "name-from-attribute" attribute is required for a nested variable'));
}
tag.addNestedVariable(nestedVariable);
},
"name": {
_type: STRING,
_targetProp: "name"
},
"name-from-attribute": {
_type: STRING,
_targetProp: "nameFromAttribute"
},
"name-from-attr": {
_type: STRING,
_targetProp: "nameFromAttribute"
}
};
var handlers = {
"raptor-taglib": {
_type: OBJECT,
_begin: function() {
var newTaglib = new Taglib();
if (!taglib) {
taglib = newTaglib;
}
return newTaglib;
},
"attribute": attributeHandler,
"tlib-version": {
_type: STRING,
_targetProp: "version"
},
"short-name": {
_type: STRING,
_targetProp: "shortName"
},
"uri": {
_type: STRING,
_set: function(taglib, name, value, context) {
if (!taglib.uri) {
taglib.uri = value;
}
else {
taglib.addAlias(value);
}
}
},
"prefix": {
_type: STRING
},
"tag": {
_type: OBJECT,
_begin: function() {
return new Tag();
},
_end: function(tag) {
if (tag.uri === undefined) {
tag.uri = taglib.uri;
}
taglib.addTag(tag);
if (tag.id) {
tagsById[tag.id] = tag;
}
},
"name": {
_type: STRING,
_targetProp: "name"
},
"uri": {
_type: STRING,
_set: function(tag, name, value, context) {
tag.uri = value || '';
}
},
"id": {
_type: STRING
},
"preserveSpace": {
_type: BOOLEAN,
_targetProp: "preserveWhitespace"
},
"preserve-space": {
_type: BOOLEAN,
_targetProp: "preserveWhitespace"
},
"preserve-whitespace": {
_type: BOOLEAN,
_targetProp: "preserveWhitespace"
},
"preserveWhitespace": {
_type: BOOLEAN,
_targetProp: "preserveWhitespace"
},
"extends": {
_type: STRING,
_targetProp: "extends"
},
"handler-class": {
_type: STRING,
_targetProp: "handlerClass"
},
"renderer": {
_type: STRING,
_targetProp: "handlerClass"
},
"template": {
_type: STRING,
_targetProp: "template"
},
"node-class": {
_type: STRING,
_targetProp: "nodeClass"
},
"dynamic-attributes": {
_type: BOOLEAN,
_targetProp: "dynamicAttributes"
},
"dynamic-attributes-remove-dashes": {
_type: BOOLEAN,
_targetProp: "dynamicAttributesRemoveDashes"
},
"<attribute>": attributeHandler,
"<static-property>": {
_type: OBJECT,
_begin: function() {
return new Property();
},
_end: function(prop, tag) {
if (!prop.name) {
throw raptor.createError(new Error('The "name" property is required for a <static-property>'));
}
if (!prop.hasOwnProperty('value')) {
throw raptor.createError(new Error('The "value" property is required for a <static-property>'));
}
tag.addStaticProperty(prop);
},
"name": {
_type: STRING
},
"type": {
_type: STRING
},
"value": {
_type: STRING
}
},
"<nested-tag>": {
_type: OBJECT,
_begin: function() {
return new Tag();
},
_end: function(nestedTag, tag) {
if (nestedTag.uri === null || nestedTag.uri === undefined) {
nestedTag.uri = taglib.uri;
}
nestedTag.targetProperty = nestedTag.targetProperty || nestedTag.name;
if (!nestedTag.name) {
throw raptor.createError(new Error('The "name" property is required for a <nested-tag>'));
}
tag.addNestedTag(nestedTag);
},
"name": {
_type: STRING
},
"type": {
_type: STRING
},
"target-property": {
_type: STRING,
_targetProp: "targetProperty"
}
},
"nested-variable": variableHandler, // Issue #28: "nested-variable" deprecated. "variable" should be used instead.
"variable": variableHandler,
"imported-variable": importVariableHandler, // Issue #28: "imported-variable" deprecated. "import-variable" should be used instead.
"import-variable": importVariableHandler,
"transformer-class": {
_type: STRING,
_set: function(tag, name, value) {
var transformer = new Transformer();
transformer.className = value;
tag.addTransformer(transformer);
}
},
"transformer": {
_type: OBJECT,
_begin: function() {
return new Transformer();
},
_end: function(transformer, tag) {
tag.addTransformer(transformer);
},
"class-name": {
_type: STRING,
_targetProp: "className"
},
"after": {
_type: STRING,
_targetProp: "after"
},
"before": {
_type: STRING,
_targetProp: "before"
},
"<properties>": {
_type: OBJECT,
_begin: function(parent) {
return (parent.properties = {});
},
"<*>": {
_type: STRING
}
}
}
},
//end "tag"
"text-transformer": {
_type: OBJECT,
_begin: function() {
return new Transformer();
},
_end: function(textTransformer) {
taglib.addTextTransformer(textTransformer);
},
"class-name": {
_type: STRING,
_targetProp: "className"
}
},
"import-taglib": {
_type: OBJECT,
_begin: function() {
return {};
},
_end: function(importedTaglib) {
var path = importedTaglib.path,
importedXmlSource,
importedTaglibResource;
if (path.startsWith('/')) {
importedTaglibResource = require('raptor/resources').findResource(path);
}
else {
importedTaglibResource = taglibResource.resolve(path);
}
if (!importedTaglibResource.exists()) {
throw raptor.createError(new Error('Imported taglib with path "' + path + '" not found in taglib at path "' + filePath + '"'));
}
require('raptor/templating/compiler').recordLoadedTaglib(importedTaglibResource);
importedXmlSource = importedTaglibResource.readAsString();
objectMapper.read(
importedXmlSource,
importedTaglibResource.getURL(),
handlers);
},
"path": {
_type: STRING
}
},
"function": {
_type: OBJECT,
_begin: function() {
return new Function();
},
_end: function(func) {
taglib.addFunction(func);
},
"name": {
_type: STRING
},
"class": {
_type: STRING,
_targetProp: "functionClass"
},
"bind-to-context": {
_type: BOOLEAN,
_targetProp: "bindToContext"
}
},
"helper-object": {
_type: OBJECT,
_begin: function() {
return new HelperObject();
},
_end: function(helperObject) {
taglib.setHelperObject(helperObject);
},
"module-name": {
_type: STRING,
_targetProp: "moduleName"
},
"class-name": {
_type: STRING,
_targetProp: "className"
}
}
}
};
objectMapper.read(
src,
filePath,
handlers);
taglib.forEachTag(function(tag) {
handleTagExtends(tag);
});
return taglib;
}
};
return TaglibXmlLoader;
});