diff --git a/src/xpath/expr-context.ts b/src/xpath/expr-context.ts index 60da862..d075a53 100644 --- a/src/xpath/expr-context.ts +++ b/src/xpath/expr-context.ts @@ -88,6 +88,7 @@ export class ExprContext { constructor( nodeList: XNode[], outputNodeList: XNode[], + xsltVersion: '1.0' | '2.0' | '3.0' = '1.0', opt_position?: number, opt_outputPosition?: number, opt_outputDepth?: number, @@ -102,6 +103,8 @@ export class ExprContext { ) { this.nodeList = nodeList; this.outputNodeList = outputNodeList; + this.xsltVersion = xsltVersion; + this.position = opt_position || 0; this.outputPosition = opt_outputPosition || 0; @@ -158,6 +161,7 @@ export class ExprContext { return new ExprContext( opt_nodeList || this.nodeList, opt_outputNodeList || this.outputNodeList, + this.xsltVersion, typeof opt_position !== 'undefined' ? opt_position : this.position, typeof opt_outputPosition !== 'undefined' ? opt_outputPosition : this.outputPosition, this.outputDepth, @@ -176,6 +180,7 @@ export class ExprContext { return new ExprContext( this.nodeList, opt_outputNodeList || this.outputNodeList, + this.xsltVersion, this.position, typeof opt_outputPosition !== 'undefined' ? opt_outputPosition : this.outputPosition, typeof opt_outputDepth !== 'undefined' ? opt_outputDepth : this.outputDepth, diff --git a/src/xpath/expressions/function-call-expr.ts b/src/xpath/expressions/function-call-expr.ts index af9505d..2690c58 100644 --- a/src/xpath/expressions/function-call-expr.ts +++ b/src/xpath/expressions/function-call-expr.ts @@ -35,6 +35,7 @@ import { formatNumber } from '../functions'; import { extCardinal, extIf, extJoin } from '../functions/non-standard'; +import { lowerCase, upperCase } from '../functions/standard-20'; import { BooleanValue } from '../values/boolean-value'; import { Expression } from './expression'; @@ -58,6 +59,7 @@ export class FunctionCallExpr extends Expression { lang, last, 'local-name': localName, + 'lower-case': lowerCase, matches, name: _name, 'namespace-uri': namespaceUri, @@ -76,6 +78,7 @@ export class FunctionCallExpr extends Expression { 'string-length': stringLength, translate, true: _true, + 'upper-case': upperCase, // TODO(mesch): The following functions are custom. There is a // standard that defines how to add functions, which should be diff --git a/src/xpath/functions/standard-20.ts b/src/xpath/functions/standard-20.ts new file mode 100644 index 0000000..717c6fc --- /dev/null +++ b/src/xpath/functions/standard-20.ts @@ -0,0 +1,15 @@ +import { ExprContext } from "../expr-context"; +import { StringValue } from "../values"; +import { assert } from "./internal-functions"; + +export function upperCase(context: ExprContext) { + assert(['2.0', '3.0'].includes(context.xsltVersion)); + const str: string = this.args[0].evaluate(context).stringValue(); + return new StringValue(str.toUpperCase()); +} + +export function lowerCase(context: ExprContext) { + assert(['2.0', '3.0'].includes(context.xsltVersion)); + const str: string = this.args[0].evaluate(context).stringValue(); + return new StringValue(str.toLowerCase()); +} diff --git a/src/xpath/values/boolean-value.ts b/src/xpath/values/boolean-value.ts index d448347..0f75f48 100644 --- a/src/xpath/values/boolean-value.ts +++ b/src/xpath/values/boolean-value.ts @@ -9,7 +9,7 @@ export class BooleanValue implements NodeValue { this.type = 'boolean'; } - stringValue() { + stringValue(): string { return `${this.value}`; } diff --git a/src/xpath/values/node-set-value.ts b/src/xpath/values/node-set-value.ts index bde7def..2b6b631 100644 --- a/src/xpath/values/node-set-value.ts +++ b/src/xpath/values/node-set-value.ts @@ -10,7 +10,7 @@ export class NodeSetValue implements NodeValue { this.type = 'node-set'; } - stringValue() { + stringValue(): string { if (this.value.length === 0) { return ''; } diff --git a/src/xpath/values/number-value.ts b/src/xpath/values/number-value.ts index ef2dcad..37ea372 100644 --- a/src/xpath/values/number-value.ts +++ b/src/xpath/values/number-value.ts @@ -9,7 +9,7 @@ export class NumberValue implements NodeValue { this.type = 'number'; } - stringValue() { + stringValue(): string { return `${this.value}`; } diff --git a/src/xpath/values/string-value.ts b/src/xpath/values/string-value.ts index 573236d..b1f56df 100644 --- a/src/xpath/values/string-value.ts +++ b/src/xpath/values/string-value.ts @@ -9,8 +9,8 @@ export class StringValue implements NodeValue { this.type = 'string'; } - stringValue() { - return this.value; + stringValue(): string { + return String(this.value); } booleanValue() { diff --git a/tests/xpath/functions.test.tsx b/tests/xpath/functions.test.tsx index dcd44d0..72be548 100644 --- a/tests/xpath/functions.test.tsx +++ b/tests/xpath/functions.test.tsx @@ -21,245 +21,268 @@ describe('XPath Functions', () => { xmlParser = new XmlParser(); }); - it('current', () => { - const xml = xmlParser.xmlParse(test); - const xsltDefinition = xmlParser.xmlParse( - - - - - - ); - - const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); - - assert.equal(outXmlString, 'test'); - }); - - describe('format-number', () => { - xmlParser = new XmlParser(); - const xml = xmlParser.xmlParse(); - - it('Trivial', () => { + describe('1.0', () => { + it('current', () => { + const xml = xmlParser.xmlParse(test); const xsltDefinition = xmlParser.xmlParse( - + ); const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); - assert.equal(outXmlString, '500100'); + assert.equal(outXmlString, 'test'); }); - it('Decimal, only integer part', () => { + describe('format-number', () => { + xmlParser = new XmlParser(); + const xml = xmlParser.xmlParse(); + + it('Trivial', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, '500100'); + }); + + it('Decimal, only integer part', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, '500100'); + }); + + it('Decimal, everything', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, '500100.2'); + }); + + it('Decimal, mask with thousand separator, everything', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, '500,100.2'); + }); + + it('Decimal, mask with filling zeroes', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, '500100.200'); + }); + + it('NaN', () => { + const xsltDefinition = xmlParser.xmlParse( + + + + + + ); + + const xsltClass = new Xslt(); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.equal(outXmlString, 'NaN'); + }); + }); + + it('generate-id, trivial', () => { + const xml = xmlParser.xmlParse(); const xsltDefinition = xmlParser.xmlParse( - + + + ); const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); - assert.equal(outXmlString, '500100'); + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + assert.ok(outXmlString); }); - it('Decimal, everything', () => { + it.skip('generate-id, complete', () => { const xsltDefinition = xmlParser.xmlParse( - - + + + + + + + + + + + + + + + ); - const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition + const xml = xmlParser.xmlParse( + + Then with expanded wings he steers his flight +
+ "Incumbent on the Dusky Air" + +
+ Aloft, incumbent on the dusky Air + + That felt unusual weight, till on dry Land +
+ "He Lights" + +
+ He lights, if it were Land that ever burned + + With solid, as the Lake with liquid fire +
+ "The Lake with Liquid Fire" + +
+
+
+
); - assert.equal(outXmlString, '500100.2'); + const xsltClass = new Xslt(); + + const outXmlString = xsltClass.xsltProcess(xml, xsltDefinition); + + // Uncomment below to see the results + // console.log(outXmlString); + assert.ok(!outXmlString); }); - it('Decimal, mask with thousand separator, everything', () => { - const xsltDefinition = xmlParser.xmlParse( - - - - - + it('translate', () => { + const xmlString = ( + + + + ); - const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition + const xsltString = ( + + + + + ); - assert.equal(outXmlString, '500,100.2'); - }); + const xsltClass = new Xslt(); - it('Decimal, mask with filling zeroes', () => { - const xsltDefinition = xmlParser.xmlParse( - - - - - - ); + const xml = xmlParser.xmlParse(xmlString); + const xslt = xmlParser.xmlParse(xsltString); - const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); + const outXmlString = xsltClass.xsltProcess(xml, xslt); - assert.equal(outXmlString, '500100.200'); + // Uncomment below to see the results + // console.log(outXmlString); + assert.ok(!outXmlString); }); + }); - it('NaN', () => { - const xsltDefinition = xmlParser.xmlParse( - + describe('2.0', () => { + it('upper-case', () => { + const xmlString = ( + + ); + + const xsltString = ( + - + - ); + ) const xsltClass = new Xslt(); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); - - assert.equal(outXmlString, 'NaN'); - }); - }); - - it('generate-id, trivial', () => { - const xml = xmlParser.xmlParse(); - const xsltDefinition = xmlParser.xmlParse( - - - - - - - - ); - const xsltClass = new Xslt(); + const xml = xmlParser.xmlParse(xmlString); + const xslt = xmlParser.xmlParse(xsltString); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); + const outXmlString = xsltClass.xsltProcess(xml, xslt); - assert.ok(outXmlString); - }); - - it.skip('generate-id, complete', () => { - const xsltDefinition = xmlParser.xmlParse( - - + assert.equal(outXmlString, 'LILY'); + }); - - - - - - - - + it('lower-case', () => { + const xmlString = ( + + ); - - - - - + const xsltString = ( + + + + + + ) - - ); - - const xml = xmlParser.xmlParse( - - Then with expanded wings he steers his flight -
- "Incumbent on the Dusky Air" - -
- Aloft, incumbent on the dusky Air - - That felt unusual weight, till on dry Land -
- "He Lights" - -
- He lights, if it were Land that ever burned - - With solid, as the Lake with liquid fire -
- "The Lake with Liquid Fire" - -
-
-
-
- ); + const xsltClass = new Xslt(); - const xsltClass = new Xslt(); + const xml = xmlParser.xmlParse(xmlString); + const xslt = xmlParser.xmlParse(xsltString); - const outXmlString = xsltClass.xsltProcess( - xml, - xsltDefinition - ); + const outXmlString = xsltClass.xsltProcess(xml, xslt); - console.log(outXmlString) - assert.ok(!outXmlString); + assert.equal(outXmlString, 'lily'); + }); }); - - it('translate', () => { - const xmlString = ( - - - - - ); - - const xsltString = - - - - - - const xsltClass = new Xslt(); - - const xml = xmlParser.xmlParse(xmlString); - const xslt = xmlParser.xmlParse(xsltString); - - const outXmlString = xsltClass.xsltProcess( - xml, - xslt - ); - - console.log(outXmlString) - assert.ok(!outXmlString); - }) });