Skip to content

Commit 2188a38

Browse files
Decimal format (#68)
* Implementing decimal format settings. * Implementing XPath's `format-number`. * Reverting `launch.json`.
1 parent 99ae262 commit 2188a38

18 files changed

+371
-49
lines changed

src/xpath/expressions/binary-expr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { xmlValue } from "../../dom";
2-
import { ExprContext } from "../expr-context";
2+
import { ExprContext } from "../../xslt/expr-context";
33
import { BooleanValue } from "../values/boolean-value";
44
import { NumberValue } from "../values/number-value";
55
import { Expression } from "./expression";

src/xpath/expressions/function-call-expr.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ExprContext } from '../expr-context';
1+
import { ExprContext } from '../../xslt/expr-context';
22
import {
33
count,
44
generateId,
@@ -31,7 +31,8 @@ import {
3131
floor,
3232
ceiling,
3333
round,
34-
current
34+
current,
35+
formatNumber
3536
} from '../functions';
3637
import { extCardinal, extIf, extJoin } from '../functions/non-standard';
3738
import { BooleanValue } from '../values/boolean-value';
@@ -50,6 +51,7 @@ export class FunctionCallExpr extends Expression {
5051
current,
5152
'ends-with': endsWith,
5253
false: _false,
54+
'format-number': formatNumber,
5355
floor,
5456
'generate-id': generateId,
5557
id,

src/xpath/expressions/location-expr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ExprContext } from "../expr-context";
1+
import { ExprContext } from "../../xslt/expr-context";
22
import { NodeSetValue } from "../values/node-set-value";
33
import { NodeTestAny } from "../node-test-any";
44
import { xPathAxis } from "../tokens";

src/xpath/expressions/step-expr.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DOM_ATTRIBUTE_NODE } from '../../constants';
22
import { XNode } from '../../dom';
3-
import { ExprContext } from '../expr-context';
3+
import { ExprContext } from '../../xslt/expr-context';
44
import { NodeSetValue } from '../values/node-set-value';
55
import { NodeTestAny } from '../node-test-any';
66
import { xPathAxis } from '../tokens';

src/xpath/functions/non-standard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { xmlValue } from "../../dom";
2-
import { ExprContext } from "../expr-context";
2+
import { ExprContext } from "../../xslt/expr-context";
33
import { NodeSetValue, StringValue } from "../values";
44
import { assert } from "./internal-functions";
55

src/xpath/functions/standard.ts

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { XNode, xmlValue } from "../../dom";
2-
import { ExprContext } from "../expr-context";
2+
import { ExprContext } from "../../xslt/expr-context";
3+
import { XsltDecimalFormatSettings } from "../../xslt/xslt-decimal-format-settings";
34
import { BooleanValue, NodeSetValue, NumberValue, StringValue } from "../values";
45
import { assert, regExpEscape } from "./internal-functions";
56

@@ -66,7 +67,7 @@ export function current(context: ExprContext) {
6667
}
6768

6869
export function endsWith(context: ExprContext) {
69-
assert(this.args.length == 2);
70+
assert(this.args.length === 2);
7071
const s0 = this.args[0].evaluate(context).stringValue();
7172
const s1 = this.args[1].evaluate(context).stringValue();
7273
const re = new RegExp(`${regExpEscape(s1)}$`);
@@ -78,6 +79,130 @@ export function _false() {
7879
return new BooleanValue(false);
7980
}
8081

82+
/**
83+
* Formats the integer part of a number. Used by `formatNumber`.
84+
* @param _number The integer part of the number.
85+
* @param _mask The mask provided.
86+
* @returns The formatted integer part of the number as a string.
87+
*/
88+
function formatNumberIntegerPart(_number: string, _mask: string, settings: XsltDecimalFormatSettings): string {
89+
let formattedIntegerPart: string = "";
90+
let lastMaskPlaceholder: string = "";
91+
let numberPosition: number = _number.length - 1;
92+
93+
for (let i = _mask.length - 1; i >= 0; i--) {
94+
lastMaskPlaceholder = _mask[i];
95+
switch (lastMaskPlaceholder) {
96+
case '#':
97+
formattedIntegerPart = _number[numberPosition] + formattedIntegerPart;
98+
numberPosition--;
99+
break;
100+
case '0':
101+
formattedIntegerPart = _number[numberPosition] + formattedIntegerPart;
102+
numberPosition--;
103+
break;
104+
case ',':
105+
formattedIntegerPart = settings.groupingSeparator + formattedIntegerPart;
106+
break;
107+
}
108+
}
109+
110+
for (; numberPosition >= 0; numberPosition--) {
111+
formattedIntegerPart = _number[numberPosition] + formattedIntegerPart;
112+
}
113+
114+
return formattedIntegerPart;
115+
}
116+
117+
/**
118+
* Formats the decimal part of a number. Used by `formatNumber`.
119+
* @param _number The decimal part of the number.
120+
* @param _mask The mask provided.
121+
* @returns The formatted decimal part of the number as a string.
122+
*/
123+
function formatNumberDecimalPart(_number: string, _mask?: string, settings?: XsltDecimalFormatSettings): string {
124+
let formattedDecimalPart: string = "";
125+
// eslint-disable-next-line no-unused-vars
126+
let _settings: XsltDecimalFormatSettings = settings;
127+
if (_mask === null || _mask === undefined) {
128+
return formattedDecimalPart;
129+
}
130+
131+
let i = 0;
132+
for (; i < _mask.length; i++) {
133+
switch (_mask[i]) {
134+
case '#':
135+
formattedDecimalPart += _number[i];
136+
137+
break;
138+
case '0':
139+
if (i >= _number.length) {
140+
formattedDecimalPart += '0';
141+
} else {
142+
formattedDecimalPart += _number[i];
143+
}
144+
145+
break;
146+
}
147+
}
148+
149+
return formattedDecimalPart;
150+
}
151+
152+
/**
153+
* XPath `format-number` function implementation.
154+
* @param context The Expression Context.
155+
* @returns A formatted number as string.
156+
*/
157+
export function formatNumber(context: ExprContext): StringValue {
158+
assert(this.args.length >= 2 && this.args.length < 4);
159+
const firstArgument = this.args[0].evaluate(context).stringValue();
160+
const secondArgument = this.args[1].evaluate(context).stringValue();
161+
const numberTest = parseFloat(firstArgument);
162+
if (isNaN(numberTest)) {
163+
return new StringValue(context.decimalFormatSettings.naN);
164+
}
165+
166+
const numberParts = String(numberTest).split('.');
167+
const maskParts = secondArgument.split('.');
168+
switch (numberParts.length) {
169+
case 1: // Integer
170+
return new StringValue(
171+
formatNumberIntegerPart(
172+
numberParts[0],
173+
maskParts[0],
174+
context.decimalFormatSettings
175+
)
176+
);
177+
case 2: // Decimal
178+
const decimalPart = formatNumberDecimalPart(
179+
numberParts[1],
180+
maskParts.length === 2 ? maskParts[1] : undefined,
181+
context.decimalFormatSettings
182+
);
183+
184+
if (decimalPart.length === 0) {
185+
return new StringValue(
186+
formatNumberIntegerPart(
187+
numberParts[0],
188+
maskParts[0],
189+
context.decimalFormatSettings
190+
)
191+
);
192+
}
193+
194+
return new StringValue(
195+
formatNumberIntegerPart(
196+
numberParts[0],
197+
maskParts[0],
198+
context.decimalFormatSettings
199+
) +
200+
context.decimalFormatSettings.decimalSeparator +
201+
decimalPart
202+
);
203+
}
204+
}
205+
81206
export function floor(context: ExprContext) {
82207
assert(this.args.length === 1);
83208
const num = this.args[0].evaluate(context).numberValue();

src/xpath/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './expr-context';
1+
export * from '../xslt/expr-context';
22
export * from './node-test-any';
33
export * from './node-test-comment';
44
export * from './node-test-element-or-attribute';

src/xpath/node-test-comment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DOM_COMMENT_NODE } from "../constants";
2-
import { ExprContext } from "./expr-context";
2+
import { ExprContext } from "../xslt/expr-context";
33
import { BooleanValue } from "./values/boolean-value";
44

55
export class NodeTestComment {

src/xpath/node-test-element-or-attribute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DOM_ATTRIBUTE_NODE, DOM_ELEMENT_NODE } from "../constants";
2-
import { ExprContext } from "./expr-context";
2+
import { ExprContext } from "../xslt/expr-context";
33
import { BooleanValue } from "./values/boolean-value";
44

55
export class NodeTestElementOrAttribute {

src/xpath/node-test-name.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ExprContext } from "./expr-context";
1+
import { ExprContext } from "../xslt/expr-context";
22
import { BooleanValue } from "./values/boolean-value";
33

44
export class NodeTestName {

0 commit comments

Comments
 (0)