marko/compiler/lib/TaglibXmlLoader.js
2014-04-02 14:11:43 -06:00

407 lines
16 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.
*/
'use strict';
var createError = require('raptor-util').createError;
var objectMapper = require('raptor-xml/object-mapper');
var regexp = require('raptor-regexp');
var Taglib = require('./Taglib');
var Tag = Taglib.Tag;
var Attribute = Taglib.Attribute;
var NestedVariable = Taglib.NestedVariable;
var ImportedVariable = Taglib.ImportedVariable;
var Transformer = Taglib.Transformer;
var Func = Taglib.Function;
var HelperObject = Taglib.HelperObject;
var nodePath = require('path');
var fs = require('fs');
var STRING = 'string';
var BOOLEAN = 'boolean';
var OBJECT = 'object';
function TaglibXmlLoader(src, filePath) {
this.src = src;
this.filePath = filePath;
}
TaglibXmlLoader.load = function (src, filePath) {
var loader = new TaglibXmlLoader(src, filePath);
return loader.load();
};
TaglibXmlLoader.prototype = {
load: function () {
var src = this.src;
var filePath = this.filePath;
var dirname = nodePath.dirname(filePath);
var tagsById = {};
function resolvePath(path) {
var resolvedPath = nodePath.resolve(dirname, path);
if (!resolvedPath.endsWith('.js')) {
resolvedPath += '.js';
}
if (!fs.existsSync(resolvedPath)) {
throw new Error('File does not exist: ' + resolvedPath);
}
return resolvedPath;
}
function handleTagExtends(subTag) {
var extendsId = subTag['extends'];
if (!extendsId) {
return;
}
delete subTag['extends'];
var superTag = tagsById[extendsId];
if (!superTag) {
throw 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) {
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'
},
'namespace': { _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 },
'remove-dashes': {
_type: BOOLEAN,
_targetProp: 'removeDashes'
}
};
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 createError(new Error('The "target-property" attribute is required for an imported variable'));
}
if (!importedVariable.expression) {
throw createError(new Error('The "expression" attribute is required for an imported variable'));
}
tag.addImportedVariable(importedVariable);
},
'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 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(filePath);
if (!taglib) {
taglib = newTaglib;
}
taglib.addInputFile(filePath);
return newTaglib;
},
'attribute': attributeHandler,
'tlib-version': {
_type: STRING,
_targetProp: 'version'
},
'uri': {
_type: STRING,
_set: function (taglib, name, value, context) {
taglib.addNamespace(value);
}
},
'namespace': {
_type: STRING,
_set: function (taglib, name, value, context) {
taglib.addNamespace(value);
}
},
'short-name': {
_type: STRING,
_set: function (taglib, name, value, context) {
taglib.addNamespace(value);
}
},
'prefix': {
_type: STRING,
_set: function (taglib, name, value, context) {
taglib.addNamespace(value);
}
},
'tag': {
_type: OBJECT,
_begin: function () {
return new Tag(taglib);
},
_end: function (tag) {
if (tag.namespace === undefined) {
tag.namespace = taglib.namespace;
}
tag.filename = filePath;
tag.dirname = dirname;
taglib.addTag(tag);
if (tag.id) {
tagsById[tag.id] = tag;
}
},
'name': {
_type: STRING,
_targetProp: 'name'
},
'namespace': {
_type: STRING,
_set: function (tag, name, value, context) {
tag.namespace = 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,
_set: function (tag, name, value, context) {
tag.renderer = resolvePath(value);
}
},
'renderer': {
_type: STRING,
_targetProp: 'renderer',
_set: function (tag, name, value, context) {
tag.renderer = resolvePath(value);
}
},
'template': {
_type: STRING,
_targetProp: 'template'
},
'node-class': {
_type: STRING,
_set: function (tag, name, path) {
tag.nodeClass = resolvePath(path);
}
},
'<attribute>': attributeHandler,
'nested-variable': variableHandler,
'variable': variableHandler,
'imported-variable': importVariableHandler,
'import-variable': importVariableHandler,
'transformer-path': {
_type: STRING,
_set: function (tag, name, path) {
var transformer = new Transformer();
transformer.dirname = dirname;
transformer.path = resolvePath(path);
tag.addTransformer(transformer);
}
},
'transformer': {
_type: OBJECT,
_begin: function () {
return new Transformer();
},
_end: function (transformer, tag) {
transformer.dirname = dirname;
tag.addTransformer(transformer);
},
'path': {
_type: STRING,
_set: function (transformer, name, path) {
transformer.path = resolvePath(path);
}
},
'after': {
_type: STRING,
_targetProp: 'after'
},
'before': {
_type: STRING,
_targetProp: 'before'
},
'name': {
_type: STRING,
_targetProp: 'name'
},
'<properties>': {
_type: OBJECT,
_begin: function (parent) {
return (parent.properties = {});
},
'<*>': { _type: STRING }
}
}
},
'text-transformer': {
_type: OBJECT,
_begin: function () {
return new Transformer();
},
_end: function (textTransformer) {
taglib.addTextTransformer(textTransformer);
},
'path': {
_type: STRING,
_set: function (transformer, name, path) {
transformer.path = resolvePath(path);
}
}
},
'import-taglib': {
_type: OBJECT,
_begin: function () {
return {};
},
_end: function (importedTaglib) {
var path = resolvePath(importedTaglib.path);
taglib.importPaths.push(path);
if (!fs.existsSync(path)) {
throw createError(new Error('Imported taglib with path "' + path + '" not found in taglib at path "' + filePath + '"'));
}
var importedXmlSource = fs.readFileSync(path);
require('../work-dir').recordLoadedTaglib(path);
var oldDirname = dirname;
dirname = nodePath.dirname(path);
objectMapper.read(importedXmlSource, path, handlers);
dirname = oldDirname;
},
'path': { _type: STRING }
},
'function': {
_type: OBJECT,
_begin: function () {
return new Func();
},
_end: function (func) {
taglib.addFunction(func);
},
'name': { _type: STRING },
'path': {
_type: STRING,
_set: function (func, name, path) {
func.path = resolvePath(path);
}
},
'bind-to-context': {
_type: BOOLEAN,
_targetProp: 'bindToContext'
}
},
'helper-object': {
_type: OBJECT,
_begin: function () {
return new HelperObject();
},
_end: function (helperObject) {
taglib.setHelperObject(helperObject);
},
'path': {
_type: STRING
}
}
}
};
objectMapper.read(src, filePath, handlers);
taglib.forEachTag(function (tag) {
handleTagExtends(tag);
});
taglib.dirname = dirname;
return taglib;
}
};
module.exports = TaglibXmlLoader;